File Coverage

blib/lib/MBclient.pm
Criterion Covered Total %
statement 108 381 28.3
branch 0 120 0.0
condition 0 24 0.0
subroutine 36 64 56.2
pod 18 18 100.0
total 162 607 26.6


line stmt bran cond sub pod time code
1             # Perl module: Client ModBus / TCP class 1
2             # Version: 1.56
3             # Website: https://github.com/sourceperl/MBclient/
4             # Date: 2014-08-05
5             # License: MIT (http://http://opensource.org/licenses/mit-license.php)
6             # Description: Client ModBus / TCP command line
7             # Support functions 3 and 16 (class 0)
8             # 1,2,4,5,6 (Class 1)
9             # Charset: us-ascii, unix end of line
10              
11             # todo
12             # - add support for MEI function
13             #
14              
15             package MBclient;
16              
17             ## Required Modules
18              
19 1     1   36691 use 5.006_001;
  1         3  
  1         40  
20 1     1   5 use strict;
  1         1  
  1         33  
21 1     1   5 use warnings;
  1         11  
  1         56  
22 1     1   5 use vars qw($AUTOLOAD $VERSION @ISA @EXPORT);
  1         2  
  1         83  
23 1     1   5 use Exporter;
  1         2  
  1         77  
24             @ISA = qw(Exporter);
25             @EXPORT = qw(MODBUS_TCP MODBUS_RTU
26             EXP_ILLEGAL_FUNCTION EXP_DATA_ADDRESS EXP_DATA_VALUE
27             EXP_SLAVE_DEVICE_FAILURE EXP_ACKNOWLEDGE EXP_SLAVE_DEVICE_BUSY
28             EXP_MEMORY_PARITY_ERROR EXP_GATEWAY_PATH_UNAVAILABLE
29             EXP_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND
30             MB_NO_ERR MB_RESOLVE_ERR MB_CONNECT_ERR MB_SEND_ERR
31             MB_RECV_ERR MB_TIMEOUT_ERR MB_FRAME_ERR MB_EXCEPT_ERR);
32 1     1   946 use Socket;
  1         4581  
  1         728  
33 1     1   931 use bytes;
  1         10  
  1         4  
34              
35             our $VERSION = '1.56';
36              
37             ##
38             ## Constant
39             ##
40              
41             ## ModBus/TCP
42 1     1   39 use constant MODBUS_PORT => 502;
  1         1  
  1         81  
43             ## ModBus RTU
44 1     1   5 use constant FRAME_RTU_MAXSIZE => 512;
  1         2  
  1         40  
45             ## Modbus mode
46 1     1   5 use constant MODBUS_TCP => 1;
  1         2  
  1         39  
47 1     1   5 use constant MODBUS_RTU => 2;
  1         2  
  1         40  
48             ## Modbus function code
49             # standard
50 1     1   8 use constant READ_COILS => 0x01;
  1         2  
  1         36  
51 1     1   5 use constant READ_DISCRETE_INPUTS => 0x02;
  1         2  
  1         40  
52 1     1   11 use constant READ_HOLDING_REGISTERS => 0x03;
  1         2  
  1         38  
53 1     1   5 use constant READ_INPUT_REGISTERS => 0x04;
  1         2  
  1         37  
54 1     1   5 use constant WRITE_SINGLE_COIL => 0x05;
  1         1  
  1         37  
55 1     1   4 use constant WRITE_SINGLE_REGISTER => 0x06;
  1         2  
  1         36  
56 1     1   4 use constant WRITE_MULTIPLE_REGISTERS => 0x10;
  1         2  
  1         38  
57 1     1   5 use constant MODBUS_ENCAPSULATED_INTERFACE => 0x2B;
  1         1  
  1         102  
58             ## Modbus except code
59 1     1   5 use constant EXP_ILLEGAL_FUNCTION => 0x01;
  1         2  
  1         38  
60 1     1   4 use constant EXP_DATA_ADDRESS => 0x02;
  1         2  
  1         44  
61 1     1   5 use constant EXP_DATA_VALUE => 0x03;
  1         2  
  1         37  
62 1     1   4 use constant EXP_SLAVE_DEVICE_FAILURE => 0x04;
  1         2  
  1         41  
63 1     1   5 use constant EXP_ACKNOWLEDGE => 0x05;
  1         2  
  1         36  
64 1     1   5 use constant EXP_SLAVE_DEVICE_BUSY => 0x06;
  1         2  
  1         42  
65 1     1   4 use constant EXP_MEMORY_PARITY_ERROR => 0x08;
  1         2  
  1         51  
66 1     1   5 use constant EXP_GATEWAY_PATH_UNAVAILABLE => 0x0A;
  1         1  
  1         40  
67 1     1   5 use constant EXP_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND => 0x0B;
  1         2  
  1         47  
68             ## Module error codes
69 1     1   5 use constant MB_NO_ERR => 0;
  1         2  
  1         44  
70 1     1   4 use constant MB_RESOLVE_ERR => 1;
  1         2  
  1         36  
71 1     1   4 use constant MB_CONNECT_ERR => 2;
  1         2  
  1         42  
72 1     1   4 use constant MB_SEND_ERR => 3;
  1         2  
  1         36  
73 1     1   4 use constant MB_RECV_ERR => 4;
  1         2  
  1         42  
74 1     1   5 use constant MB_TIMEOUT_ERR => 5;
  1         1  
  1         36  
75 1     1   5 use constant MB_FRAME_ERR => 6;
  1         16  
  1         41  
76 1     1   5 use constant MB_EXCEPT_ERR => 7;
  1         1  
  1         3581  
77              
78              
79             ##
80             ## Constructor.
81             ##
82              
83             sub new {
84 0     0 1   my $this = shift;
85 0   0       my $class = ref($this) || $this;
86 0           my $self = {};
87             ##
88             ## UPPERCASE items have documented accessor functions.
89             ## lowercase items are reserved for internal use.
90             ##
91 0           $self->{VERSION} = $VERSION; # version number
92 0           $self->{HOST} = undef; #
93 0           $self->{PORT} = MODBUS_PORT; #
94 0           $self->{UNIT_ID} = 1; #
95 0           $self->{LAST_ERROR} = MB_NO_ERR; # last error code
96 0           $self->{LAST_EXCEPT} = 0; # last expect code
97 0           $self->{MODE} = MODBUS_TCP; # by default modbus/tcp
98 0           $self->{sock} = undef; # socket handle
99 0           $self->{timeout} = 30; # socket timeout
100 0           $self->{hd_tr_id} = 0; # store transaction ID
101 0           $self->{debug} = 0; # enable debug trace
102             # object bless
103 0           bless $self, $class;
104 0           return $self;
105             }
106              
107             ##
108             ## Get current version number.
109             ##
110              
111             sub version {
112 0     0 1   my $self = shift;
113 0           return $self->{VERSION};
114             }
115              
116             ##
117             ## Get last error code.
118             ##
119              
120             sub last_error {
121 0     0 1   my $self = shift;
122 0           return $self->{LAST_ERROR};
123             }
124              
125             ##
126             ## Get last except code.
127             ##
128              
129             sub last_except {
130 0     0 1   my $self = shift;
131 0           return $self->{LAST_EXCEPT};
132             }
133              
134             ##
135             ## Get or set host field (IPv4 or hostname like "plc.domain.net").
136             ##
137              
138             sub host {
139 0     0 1   my $self = shift;
140 0           my $hostname = shift;
141             # return last hostname if no arg
142 0 0         return $self->{HOST} unless defined $hostname;
143             # if host is IPv4 address or valid URL
144 0 0 0       if (($hostname =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) or
145             ($hostname =~ m/^[a-z][a-z0-9\.\-]+$/)) {
146 0           $self->{HOST} = $hostname;
147             }
148 0           return $self->{HOST};
149             }
150              
151             ##
152             ## Get or set TCP port field.
153             ##
154              
155             sub port {
156 0     0 1   my $self = shift;
157 0           my $port = shift;
158             # return last hostname if no arg
159 0 0         return $self->{PORT} unless defined $port;
160             # if host is IPv4 address or valid URL
161 0 0 0       if (($port =~ m/^\d{1,5}$/) and
162             ($port < 65536)) {
163 0           $self->{PORT} = $port;
164             }
165 0           return $self->{PORT};
166             }
167              
168             ##
169             ## Get or set unit_id field.
170             ##
171              
172             sub unit_id {
173 0     0 1   my $self = shift;
174 0           my $uid = shift;
175             # return unit_id if no arg
176 0 0         return $self->{UNIT_ID} unless defined $uid;
177             # if uid is numeric, set unit_id
178 0 0         if ($uid =~ m/^\d{1,3}$/) {
179 0           $self->{UNIT_ID} = $uid;
180             }
181 0           return $self->{UNIT_ID};
182             }
183              
184             ##
185             ## Get or set modbus mode (TCP or RTU).
186             ##
187              
188             sub mode {
189 0     0 1   my $self = shift;
190 0           my $mode = shift;
191             # return mode if no arg
192 0 0         return $self->{MODE} unless defined $mode;
193             # set mode and return mode
194 0           $self->{MODE} = $mode;
195 0           return $self->{MODE};
196             }
197              
198             ##
199             ## Open TCP link.
200             ##
201              
202             sub open {
203 0     0 1   my $self = shift;
204 0 0         print 'call open()'."\n" if ($self->{debug});
205             # restart TCP if already open
206 0 0         $self->close if ($self->is_open);
207             # name resolve
208 0           my $ad_ip = inet_aton($self->{HOST});
209 0 0         unless($ad_ip) {
210 0           $self->{LAST_ERROR} = MB_RESOLVE_ERR;
211 0 0         print 'IP resolve error'."\n" if ($self->{debug});
212 0           return undef;
213             }
214             # set socket
215 0           socket($self->{sock}, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
216 0           my $connect_ok = connect($self->{sock}, sockaddr_in($self->{PORT}, $ad_ip));
217 0 0         if ($connect_ok) {
218 0           return 1;
219             } else {
220 0           $self->{sock} = undef;
221 0           $self->{LAST_ERROR} = MB_CONNECT_ERR;
222 0 0         print 'TCP connect error'."\n" if ($self->{debug});
223 0           return undef;
224             }
225             };
226              
227             ##
228             ## Check TCP link.
229             ##
230              
231             sub is_open {
232 0     0 1   my $self = shift;
233 0           return (defined $self->{sock});
234             };
235              
236             ##
237             ## Close TCP link.
238             ##
239              
240             sub close {
241 0     0 1   my $self = shift;
242 0 0         if ($self->{sock}) {
243 0           close $self->{sock};
244 0           $self->{sock} = undef;
245 0           return 1;
246             } else {
247 0           return undef;
248             }
249             };
250              
251             ##
252             ## Modbus function READ_COILS (0x01).
253             ## read_coils(bit_addr, bit_number)
254             ## return a ref to result array
255             ## or undef if error
256              
257             sub read_coils {
258 0     0 1   my $self = shift;
259 0           my $bit_addr = shift;
260 0           my $bit_nb = shift;
261             # build frame
262 0           my $tx_buffer = $self->_mbus_frame(READ_COILS, pack("nn", $bit_addr, $bit_nb));
263             # send request
264 0           my $s_send = $self->_send_mbus($tx_buffer);
265             # check error
266 0 0         return undef unless ($s_send);
267             # receive
268 0           my $f_body = $self->_recv_mbus();
269             # check error
270 0 0         return undef unless ($f_body);
271             # register extract
272 0           my ($rx_byte_count, $f_bits) = unpack 'Cb*', $f_body;
273             # read bit(s) string
274 0           my @bits = split //, $f_bits;
275 0           $#bits = $bit_nb - 1;
276 0           return \@bits;
277             }
278              
279             ##
280             ## Modbus function READ_DISCRETE_INPUTS (0x02).
281             ## read_discrete_inputs(bit_addr, bit_number)
282             ## return a ref to result array
283             ## or undef if error
284              
285             sub read_discrete_inputs {
286 0     0 1   my $self = shift;
287 0           my $bit_addr = shift;
288 0           my $bit_nb = shift;
289             # build frame
290 0           my $tx_buffer = $self->_mbus_frame(READ_DISCRETE_INPUTS, pack("nn", $bit_addr, $bit_nb));
291             # send request
292 0           my $s_send = $self->_send_mbus($tx_buffer);
293             # check error
294 0 0         return undef unless ($s_send);
295             # receive
296 0           my $f_body = $self->_recv_mbus();
297             # check error
298 0 0         return undef unless ($f_body);
299             # register extract
300 0           my ($rx_byte_count, $f_bits) = unpack 'Cb*', $f_body;
301             # read bit(s) string
302 0           my @bits = split //, $f_bits;
303 0           $#bits = $bit_nb - 1;
304 0           return \@bits;
305             }
306              
307              
308             ##
309             ## Modbus function READ_HOLDING_REGISTERS (0x03).
310             ## read_holding_registers(reg_addr, reg_number)
311             ## return a ref to result array
312             ## or undef if error
313              
314             sub read_holding_registers {
315 0     0 1   my $self = shift;
316 0           my $reg_addr = shift;
317 0           my $reg_nb = shift;
318             # build frame
319 0           my $tx_buffer = $self->_mbus_frame(READ_HOLDING_REGISTERS, pack("nn", $reg_addr, $reg_nb));
320             # send request
321 0           my $s_send = $self->_send_mbus($tx_buffer);
322             # check error
323 0 0         return undef unless ($s_send);
324             # receive
325 0           my $f_body = $self->_recv_mbus();
326             # check error
327 0 0         return undef unless ($f_body);
328             # register extract
329 0           my ($rx_reg_count, $f_regs) = unpack 'Ca*', $f_body;
330             # read 16 bits register
331 0           my @registers = unpack 'n*', $f_regs;
332 0           return \@registers;
333             }
334              
335             ##
336             ## Modbus function READ_INPUT_REGISTERS (0x04).
337             ## read_input_registers(reg_addr, reg_number)
338             ## return a ref to result array
339             ## or undef if error
340              
341             sub read_input_registers {
342 0     0 1   my $self = shift;
343 0           my $reg_addr = shift;
344 0           my $reg_nb = shift;
345             # build frame
346 0           my $tx_buffer = $self->_mbus_frame(READ_INPUT_REGISTERS, pack("nn", $reg_addr, $reg_nb));
347             # send request
348 0           my $s_send = $self->_send_mbus($tx_buffer);
349             # check error
350 0 0         return undef unless ($s_send);
351             # receive
352 0           my $f_body = $self->_recv_mbus();
353             # check error
354 0 0         return undef unless ($f_body);
355             # register extract
356 0           my ($rx_reg_count, $f_regs) = unpack 'Ca*', $f_body;
357             # read 16 bits register
358 0           my @registers = unpack 'n*', $f_regs;
359 0           return \@registers;
360             }
361              
362             ##
363             ## Modbus function WRITE_SINGLE_COIL (0x05).
364             ## write_single_coil(bit_addr, bit_value)
365             ## return 1 if write success
366             ## or undef if error
367              
368             sub write_single_coil {
369 0     0 1   my $self = shift;
370 0           my $bit_addr = shift;
371 0           my $bit_value = shift;
372             # build frame
373 0 0         $bit_value = ($bit_value) ? 0xFF : 0;
374 0           my $tx_buffer = $self->_mbus_frame(WRITE_SINGLE_COIL, pack("nCC", $bit_addr, $bit_value, 0));
375             # send request
376 0           my $s_send = $self->_send_mbus($tx_buffer);
377             # check error
378 0 0         return undef unless ($s_send);
379             # receive
380 0           my $f_body = $self->_recv_mbus();
381             # check error
382 0 0         return undef unless ($f_body);
383             # register extract
384 0           my ($rx_bit_addr, $rx_bit_value, $rx_padding) = unpack 'nCC', $f_body;
385             # check bit write
386 0 0 0       return (($rx_bit_addr == $bit_addr) and ($rx_bit_value == $bit_value)) ? 1 : undef;
387             }
388              
389             ##
390             ## Modbus function WRITE_SINGLE_REGISTER (0x06).
391             ## write_single_register(reg_addr, reg_value)
392             ## return 1 if write success
393             ## or undef if error
394              
395             sub write_single_register {
396 0     0 1   my $self = shift;
397 0           my $reg_addr = shift;
398 0           my $reg_value = shift;
399             # build frame
400 0           my $tx_buffer = $self->_mbus_frame(WRITE_SINGLE_REGISTER, pack("nn", $reg_addr, $reg_value));
401             # send request
402 0           my $s_send = $self->_send_mbus($tx_buffer);
403             # check error
404 0 0         return undef unless ($s_send);
405             # receive
406 0           my $f_body = $self->_recv_mbus();
407             # check error
408 0 0         return undef unless ($f_body);
409             # register extract
410 0           my ($rx_reg_addr, $rx_reg_value) = unpack 'nn', $f_body;
411             # check bit write
412 0 0 0       return (($rx_reg_addr == $reg_addr) and ($rx_reg_value == $reg_value)) ? 1 : undef;
413             }
414              
415             ##
416             ## Modbus function WRITE_MULTIPLE_REGISTERS (0x10).
417             ## write_multiple_registers(reg_addr, ref_to_reg_array)
418             ## return 1 if write success
419             ## or undef if error
420              
421             sub write_multiple_registers {
422 0     0 1   my $self = shift;
423 0           my $reg_addr = shift;
424 0           my $ref_array_reg = shift;
425 0           my @reg_value = @$ref_array_reg;
426             # build frame
427             # register
428 0           my $reg_nb = @reg_value;
429             # format reg value string
430 0           my $reg_val_str;
431 0           for (@reg_value) {$reg_val_str .= pack("n", $_);}
  0            
432 0           my $bytes_nb = bytes::length($reg_val_str);
433             # format modbus frame body
434 0           my $body = pack("nnC", $reg_addr, $reg_nb, $bytes_nb).$reg_val_str;
435 0           my $tx_buffer = $self->_mbus_frame(WRITE_MULTIPLE_REGISTERS, $body);
436             # send request
437 0           my $s_send = $self->_send_mbus($tx_buffer);
438             # check error
439 0 0         return undef unless ($s_send);
440             # receive
441 0           my $f_body = $self->_recv_mbus();
442             # check error
443 0 0         return undef unless ($f_body);
444             # register extract
445 0           my ($rx_reg_addr, $rx_reg_nb) = unpack 'nn', $f_body;
446             # check regs write
447 0 0         return ($rx_reg_addr == $reg_addr) ? 1 : undef;
448             }
449              
450             # Build modbus frame.
451             # _mbus_frame(function code, body)
452             # return modbus frame
453             sub _mbus_frame {
454 0     0     my $self = shift;
455 0           my $fc = shift;
456 0           my $body = shift;
457             # build frame body
458 0           my $f_body = pack("C", $fc).$body;
459             # modbus/TCP
460 0 0         if ($self->{MODE} == MODBUS_TCP) {
    0          
461             # build frame ModBus Application Protocol header (mbap)
462 0           $self->{hd_tr_id} = int(rand 65535);
463 0           my $tx_hd_pr_id = 0;
464 0           my $tx_hd_length = bytes::length($f_body) + 1;
465 0           my $f_mbap = pack("nnnC", $self->{hd_tr_id}, $tx_hd_pr_id,
466             $tx_hd_length, $self->{UNIT_ID});
467 0           return $f_mbap.$f_body;
468             # modbus RTU
469             } elsif ($self->{MODE} == MODBUS_RTU) {
470             # format [slave addr(unit_id)]frame_body[CRC16]
471 0           my $slave_ad = pack("C", $self->{UNIT_ID});
472 0           return $self->_add_crc($slave_ad.$f_body);
473             } else {
474             # unknow mode
475 0           return undef;
476             }
477             }
478              
479             # Send modbus frame.
480             # _send_mbus(frame)
481             # return $nb_byte send
482             sub _send_mbus {
483 0     0     my $self = shift;
484 0           my $frame = shift;
485             # send request
486 0           my $bytes_send = $self->_send($frame);
487 0 0         return undef unless ($bytes_send);
488             # for debug
489 0 0         $self->_pretty_dump('Tx', $frame) if ($self->{debug});
490             # return
491 0           return $bytes_send;
492             }
493              
494             # Recv modbus frame.
495             # _recv_mbus()
496             # return body (after func. code)
497             sub _recv_mbus {
498 0     0     my $self = shift;
499             ## receive
500             # vars
501 0           my ($rx_buffer,$rx_frame);
502 0           my ($rx_unit_id, $rx_bd_fc, $f_body);
503             # modbus TCP receive
504 0 0         if ($self->{MODE} == MODBUS_TCP) {
    0          
505             # 7 bytes head
506 0           $rx_buffer = $self->_recv(7);
507 0 0         return undef unless($rx_buffer);
508 0           $rx_frame = $rx_buffer;
509             # decode
510 0           my ($rx_hd_tr_id, $rx_hd_pr_id, $rx_hd_length, $rx_hd_unit_id) = unpack "nnnC", $rx_frame;
511             # check
512 0 0 0       if (!(($rx_hd_tr_id == $self->{hd_tr_id}) && ($rx_hd_pr_id == 0) &&
      0        
      0        
513             ($rx_hd_length < 256) && ($rx_hd_unit_id == $self->{UNIT_ID}))) {
514 0           $self->close;
515 0           return undef;
516             }
517             # end of frame
518 0           $rx_buffer = $self->_recv($rx_hd_length-1);
519 0 0         return undef unless($rx_buffer);
520 0           $rx_frame .= $rx_buffer;
521             # dump frame
522 0 0         $self->_pretty_dump('Rx', $rx_frame) if ($self->{debug});
523             # body decode
524 0           ($rx_bd_fc, $f_body) = unpack "Ca*", $rx_buffer;
525             # modbus RTU receive
526             } elsif ($self->{MODE} == MODBUS_RTU) {
527 0           $rx_buffer = $self->_recv(FRAME_RTU_MAXSIZE);
528 0 0         return undef unless($rx_buffer);
529 0           $rx_frame = $rx_buffer;
530             # dump frame
531 0 0         $self->_pretty_dump('Rx', $rx_frame) if ($self->{debug});
532             # body decode
533 0           ($rx_unit_id, $rx_bd_fc, $f_body) = unpack "CCa*", $rx_frame;
534             # check
535 0 0         if (!($rx_unit_id == $self->{UNIT_ID})) {
536 0           $self->close;
537 0           return undef;
538             }
539             }
540             # check except
541 0 0         if ($rx_bd_fc > 0x80) {
542             # except code
543 0           my ($exp_code) = unpack "C", $f_body;
544 0           $self->{LAST_ERROR} = MB_EXCEPT_ERR;
545 0           $self->{LAST_EXCEPT} = $exp_code;
546 0 0         print 'except (code '.$exp_code.')'."\n" if ($self->{debug});
547 0           return undef;
548             } else {
549             # return
550 0           return $f_body;
551             }
552             }
553              
554             # Send data over current socket.
555             # _send(data_to_send)
556             # return the number of bytes send
557             # or undef if error
558             sub _send {
559 0     0     my $self = shift;
560 0           my $data = shift;
561             # check link, open if need
562 0 0         unless ($self->is_open) {
563 0 0         print 'call _send() not open -> call open()'."\n" if ($self->{debug});
564 0 0         return undef unless ($self->open);
565             }
566             # send data
567 0           my $data_l = bytes::length($data);
568 0           my $send_l = send($self->{sock}, $data, 0);
569             # send error
570 0 0         if ($send_l != $data_l) {
571 0           $self->{LAST_ERROR} = MB_SEND_ERR;
572 0 0         print '_send error'."\n" if ($self->{debug});
573 0           $self->close;
574 0           return undef;
575             } else {
576 0           return $send_l;
577             }
578             }
579              
580             # Recv data over current socket.
581             # _recv(max_size)
582             # return the receive buffer
583             # or undef if error
584             sub _recv {
585 0     0     my $self = shift;
586 0           my $max_size = shift;
587             # wait for read
588 0 0         unless ($self->_can_read()) {
589 0           $self->close;
590 0           return undef;
591             }
592             # recv
593 0           my $buffer;
594 0           my $s_recv = recv($self->{sock}, $buffer, $max_size, 0);
595 0 0         unless (defined $s_recv) {
596 0           $self->{LAST_ERROR} = MB_RECV_ERR;
597 0 0         print '_recv error'."\n" if ($self->{debug});
598 0           $self->close;
599 0           return undef;
600             }
601 0           return $buffer;
602             }
603              
604             # Wait for socket read.
605             sub _can_read {
606 0     0     my $self = shift;
607 0           my $hdl_select = "";
608 0           vec($hdl_select, fileno($self->{sock}), 1) = 1;
609 0           my $_select = select($hdl_select, undef, undef, $self->{timeout});
610 0 0         if ($_select) {
611 0           return $_select;
612             } else {
613 0           $self->{LAST_ERROR} = MB_TIMEOUT_ERR;
614 0 0         print 'timeout error'."\n" if ($self->{debug});
615 0           $self->close;
616 0           return undef;
617             }
618             }
619              
620             # Compute modbus CRC16 (for RTU mode).
621             # _crc(modbus_frame)
622             # return the CRC
623             sub _crc {
624 0     0     my $self = shift;
625 0           my $frame = shift;
626 0           my $crc = 0xFFFF;
627 0           my ($chr, $lsb);
628 0           for my $i (0..bytes::length($frame)-1) {
629 0           $chr = ord(bytes::substr($frame, $i, 1));
630 0           $crc ^= $chr;
631 0           for (1..8) {
632 0           $lsb = $crc & 1;
633 0           $crc >>= 1;
634 0 0         $crc ^= 0xA001 if $lsb;
635             }
636             }
637 0           return $crc;
638             }
639              
640             # Add CRC to modbus frame (for RTU mode).
641             # _add_crc(modbus_frame)
642             # return modbus_frame_with_crc
643             sub _add_crc {
644 0     0     my $self = shift;
645 0           my $frame = shift;
646 0           my $crc = pack 'v', $self->_crc($frame);
647 0           return $frame.$crc;
648             }
649              
650             # Check the CRC of modbus RTU frame.
651             # _crc_is_ok(modbus_frame_with_crc)
652             # return true if CRC is ok
653             sub _crc_is_ok {
654 0     0     my $self = shift;
655 0           my $frame = shift;
656 0           my $crc = unpack('v', bytes::substr($frame, -2));
657 0           return ($crc == $self->_crc($frame));
658             }
659              
660             # Print modbus/TCP frame ("[header]body") or modbus RTU ("body[CRC]").
661             sub _pretty_dump {
662 0     0     my $self = shift;
663 0           my $label = shift;
664 0           my $data = shift;
665 0           my @dump = map {sprintf "%02X", $_ } unpack("C*", $data);
  0            
666             # format for TCP or RTU
667 0 0         if ($self->{MODE} == MODBUS_TCP) {
    0          
668 0           $dump[0] = "[".$dump[0];
669 0           $dump[5] = $dump[5]."]";
670             } elsif ($self->{MODE} == MODBUS_RTU) {
671 0           $dump[$#dump-1] = "[".$dump[$#dump-1];
672 0           $dump[$#dump] = $dump[$#dump]."]";
673             }
674             # print result
675 0           print $label."\n";
676 0           for (@dump) {print $_." ";}
  0            
677 0           print "\n\n";
678             }
679              
680             1;
681              
682             __END__