File Coverage

blib/lib/MBclient.pm
Criterion Covered Total %
statement 111 394 28.1
branch 0 128 0.0
condition 0 24 0.0
subroutine 37 65 56.9
pod 18 18 100.0
total 166 629 26.3


line stmt bran cond sub pod time code
1             # Perl module: Client ModBus / TCP class 1
2             # Version: 1.57
3             # Website: https://github.com/sourceperl/MBclient/
4             # Date: 2014-11-27
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   21488 use 5.006_001;
  1         3  
  1         45  
20 1     1   5 use strict;
  1         2  
  1         43  
21 1     1   5 use warnings;
  1         12  
  1         43  
22 1     1   6 use vars qw($AUTOLOAD $VERSION @ISA @EXPORT);
  1         2  
  1         85  
23 1     1   6 use Exporter;
  1         1  
  1         87  
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   724 use Socket;
  1         4165  
  1         631  
33 1     1   776 use bytes;
  1         11  
  1         5  
34              
35             our $VERSION = '1.57';
36              
37             ##
38             ## Constant
39             ##
40              
41             ## ModBus/TCP
42 1     1   53 use constant MODBUS_PORT => 502;
  1         2  
  1         92  
43             ## ModBus RTU
44 1     1   6 use constant FRAME_RTU_MAXSIZE => 256;
  1         2  
  1         50  
45             ## Modbus mode
46 1     1   6 use constant MODBUS_TCP => 1;
  1         1  
  1         47  
47 1     1   6 use constant MODBUS_RTU => 2;
  1         2  
  1         51  
48             ## Modbus function code
49             # standard
50 1     1   10 use constant READ_COILS => 0x01;
  1         1  
  1         49  
51 1     1   5 use constant READ_DISCRETE_INPUTS => 0x02;
  1         2  
  1         51  
52 1     1   15 use constant READ_HOLDING_REGISTERS => 0x03;
  1         1  
  1         50  
53 1     1   5 use constant READ_INPUT_REGISTERS => 0x04;
  1         2  
  1         47  
54 1     1   5 use constant WRITE_SINGLE_COIL => 0x05;
  1         2  
  1         45  
55 1     1   6 use constant WRITE_SINGLE_REGISTER => 0x06;
  1         1  
  1         45  
56 1     1   5 use constant WRITE_MULTIPLE_REGISTERS => 0x10;
  1         2  
  1         47  
57 1     1   5 use constant MODBUS_ENCAPSULATED_INTERFACE => 0x2B;
  1         1  
  1         47  
58             ## Modbus except code
59 1     1   7 use constant EXP_ILLEGAL_FUNCTION => 0x01;
  1         2  
  1         44  
60 1     1   6 use constant EXP_DATA_ADDRESS => 0x02;
  1         2  
  1         53  
61 1     1   6 use constant EXP_DATA_VALUE => 0x03;
  1         1  
  1         46  
62 1     1   6 use constant EXP_SLAVE_DEVICE_FAILURE => 0x04;
  1         1  
  1         48  
63 1     1   6 use constant EXP_ACKNOWLEDGE => 0x05;
  1         1  
  1         45  
64 1     1   6 use constant EXP_SLAVE_DEVICE_BUSY => 0x06;
  1         1  
  1         50  
65 1     1   5 use constant EXP_MEMORY_PARITY_ERROR => 0x08;
  1         1  
  1         52  
66 1     1   5 use constant EXP_GATEWAY_PATH_UNAVAILABLE => 0x0A;
  1         6  
  1         50  
67 1     1   6 use constant EXP_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND => 0x0B;
  1         2  
  1         55  
68             ## Module error codes
69 1     1   6 use constant MB_NO_ERR => 0;
  1         2  
  1         49  
70 1     1   6 use constant MB_RESOLVE_ERR => 1;
  1         1  
  1         50  
71 1     1   5 use constant MB_CONNECT_ERR => 2;
  1         2  
  1         49  
72 1     1   6 use constant MB_SEND_ERR => 3;
  1         1  
  1         47  
73 1     1   5 use constant MB_RECV_ERR => 4;
  1         2  
  1         50  
74 1     1   5 use constant MB_TIMEOUT_ERR => 5;
  1         1  
  1         44  
75 1     1   6 use constant MB_FRAME_ERR => 6;
  1         23  
  1         54  
76 1     1   7 use constant MB_EXCEPT_ERR => 7;
  1         1  
  1         52  
77 1     1   5 use constant MB_CRC_ERR => 8;
  1         2  
  1         3486  
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             # RTU frame min size is 5: check this here
533 0 0         if (bytes::length($rx_frame) < 5) {
534 0           $self->{LAST_ERROR} = MB_RECV_ERR;
535 0 0         print 'short frame error'."\n" if ($self->{debug});
536 0           $self->close;
537 0           return undef;
538             }
539             # check CRC
540 0 0         if (! $self->_crc_is_ok($rx_frame)) {
541 0           $self->{LAST_ERROR} = MB_CRC_ERR;
542 0 0         print 'CRC error'."\n" if ($self->{debug});
543 0           $self->close;
544 0           return undef;
545             }
546             # remove CRC
547 0           $rx_frame = bytes::substr($rx_frame, 0, -2);
548             # body decode
549 0           ($rx_unit_id, $rx_bd_fc, $f_body) = unpack "CCa*", $rx_frame;
550             # check
551 0 0         if (!($rx_unit_id == $self->{UNIT_ID})) {
552 0           $self->close;
553 0           return undef;
554             }
555             }
556             # check except
557 0 0         if ($rx_bd_fc > 0x80) {
558             # except code
559 0           my ($exp_code) = unpack "C", $f_body;
560 0           $self->{LAST_ERROR} = MB_EXCEPT_ERR;
561 0           $self->{LAST_EXCEPT} = $exp_code;
562 0 0         print 'except (code '.$exp_code.')'."\n" if ($self->{debug});
563 0           return undef;
564             } else {
565             # return
566 0           return $f_body;
567             }
568             }
569              
570             # Send data over current socket.
571             # _send(data_to_send)
572             # return the number of bytes send
573             # or undef if error
574             sub _send {
575 0     0     my $self = shift;
576 0           my $data = shift;
577             # check link, open if need
578 0 0         unless ($self->is_open) {
579 0 0         print 'call _send() not open -> call open()'."\n" if ($self->{debug});
580 0 0         return undef unless ($self->open);
581             }
582             # send data
583 0           my $data_l = bytes::length($data);
584 0           my $send_l = send($self->{sock}, $data, 0);
585             # send error
586 0 0         if ($send_l != $data_l) {
587 0           $self->{LAST_ERROR} = MB_SEND_ERR;
588 0 0         print '_send error'."\n" if ($self->{debug});
589 0           $self->close;
590 0           return undef;
591             } else {
592 0           return $send_l;
593             }
594             }
595              
596             # Recv data over current socket.
597             # _recv(max_size)
598             # return the receive buffer
599             # or undef if error
600             sub _recv {
601 0     0     my $self = shift;
602 0           my $max_size = shift;
603             # wait for read
604 0 0         unless ($self->_can_read()) {
605 0           $self->close;
606 0           return undef;
607             }
608             # recv
609 0           my $buffer;
610 0           my $s_recv = recv($self->{sock}, $buffer, $max_size, 0);
611 0 0         unless (defined $s_recv) {
612 0           $self->{LAST_ERROR} = MB_RECV_ERR;
613 0 0         print '_recv error'."\n" if ($self->{debug});
614 0           $self->close;
615 0           return undef;
616             }
617 0           return $buffer;
618             }
619              
620             # Wait for socket read.
621             sub _can_read {
622 0     0     my $self = shift;
623 0           my $hdl_select = "";
624 0           vec($hdl_select, fileno($self->{sock}), 1) = 1;
625 0           my $_select = select($hdl_select, undef, undef, $self->{timeout});
626 0 0         if ($_select) {
627 0           return $_select;
628             } else {
629 0           $self->{LAST_ERROR} = MB_TIMEOUT_ERR;
630 0 0         print 'timeout error'."\n" if ($self->{debug});
631 0           $self->close;
632 0           return undef;
633             }
634             }
635              
636             # Compute modbus CRC16 (for RTU mode).
637             # _crc(modbus_frame)
638             # return the CRC
639             sub _crc {
640 0     0     my $self = shift;
641 0           my $frame = shift;
642 0           my $crc = 0xFFFF;
643 0           my ($chr, $lsb);
644 0           for my $i (0..bytes::length($frame)-1) {
645 0           $chr = ord(bytes::substr($frame, $i, 1));
646 0           $crc ^= $chr;
647 0           for (1..8) {
648 0           $lsb = $crc & 1;
649 0           $crc >>= 1;
650 0 0         $crc ^= 0xA001 if $lsb;
651             }
652             }
653 0           return $crc;
654             }
655              
656             # Add CRC to modbus frame (for RTU mode).
657             # _add_crc(modbus_frame)
658             # return modbus_frame_with_crc
659             sub _add_crc {
660 0     0     my $self = shift;
661 0           my $frame = shift;
662 0           my $crc = pack 'v', $self->_crc($frame);
663 0           return $frame.$crc;
664             }
665              
666             # Check the CRC of modbus RTU frame.
667             # _crc_is_ok(modbus_frame_with_crc)
668             # return true if CRC is ok
669             sub _crc_is_ok {
670 0     0     my $self = shift;
671 0           my $frame = shift;
672 0           return ($self->_crc($frame) == 0);
673             }
674              
675             # Print modbus/TCP frame ("[header]body") or modbus RTU ("body[CRC]").
676             sub _pretty_dump {
677 0     0     my $self = shift;
678 0           my $label = shift;
679 0           my $data = shift;
680 0           my @dump = map {sprintf "%02X", $_ } unpack("C*", $data);
  0            
681             # format for TCP or RTU
682 0 0         if ($self->{MODE} == MODBUS_TCP) {
    0          
683 0           $dump[0] = "[".$dump[0];
684 0           $dump[5] = $dump[5]."]";
685             } elsif ($self->{MODE} == MODBUS_RTU) {
686 0           $dump[$#dump-1] = "[".$dump[$#dump-1];
687 0           $dump[$#dump] = $dump[$#dump]."]";
688             }
689             # print result
690 0           print $label."\n";
691 0           for (@dump) {print $_." ";}
  0            
692 0           print "\n\n";
693             }
694              
695             1;
696              
697             __END__