File Coverage

blib/lib/MBclient.pm
Criterion Covered Total %
statement 110 399 27.5
branch 0 132 0.0
condition 0 27 0.0
subroutine 37 66 56.0
pod 19 19 100.0
total 166 643 25.8


line stmt bran cond sub pod time code
1             # Perl module: Client ModBus / TCP class 1
2             # Version: 1.58
3             # Website: https://github.com/sourceperl/MBclient/
4             # Date: 2017-10-25
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   58156 use 5.006_001;
  1         3  
20 1     1   4 use strict;
  1         2  
  1         16  
21 1     1   4 use warnings;
  1         1  
  1         34  
22 1     1   4 use vars qw($AUTOLOAD $VERSION @ISA @EXPORT);
  1         2  
  1         51  
23 1     1   5 use Exporter;
  1         1  
  1         68  
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   517 use Socket;
  1         3036  
  1         312  
33 1     1   480 use bytes;
  1         12  
  1         6  
34              
35             our $VERSION = '1.58';
36              
37             ##
38             ## Constant
39             ##
40              
41             ## ModBus/TCP
42 1     1   38 use constant MODBUS_PORT => 502;
  1         2  
  1         85  
43             ## ModBus RTU
44 1     1   6 use constant FRAME_RTU_MAXSIZE => 256;
  1         1  
  1         36  
45             ## Modbus mode
46 1     1   9 use constant MODBUS_TCP => 1;
  1         2  
  1         32  
47 1     1   4 use constant MODBUS_RTU => 2;
  1         2  
  1         29  
48             ## Modbus function code
49             # standard
50 1     1   4 use constant READ_COILS => 0x01;
  1         1  
  1         30  
51 1     1   3 use constant READ_DISCRETE_INPUTS => 0x02;
  1         2  
  1         29  
52 1     1   4 use constant READ_HOLDING_REGISTERS => 0x03;
  1         1  
  1         31  
53 1     1   4 use constant READ_INPUT_REGISTERS => 0x04;
  1         1  
  1         30  
54 1     1   4 use constant WRITE_SINGLE_COIL => 0x05;
  1         1  
  1         28  
55 1     1   4 use constant WRITE_SINGLE_REGISTER => 0x06;
  1         1  
  1         29  
56 1     1   4 use constant WRITE_MULTIPLE_REGISTERS => 0x10;
  1         1  
  1         34  
57 1     1   4 use constant MODBUS_ENCAPSULATED_INTERFACE => 0x2B;
  1         2  
  1         29  
58             ## Modbus except code
59 1     1   3 use constant EXP_ILLEGAL_FUNCTION => 0x01;
  1         2  
  1         28  
60 1     1   4 use constant EXP_DATA_ADDRESS => 0x02;
  1         1  
  1         28  
61 1     1   4 use constant EXP_DATA_VALUE => 0x03;
  1         1  
  1         29  
62 1     1   4 use constant EXP_SLAVE_DEVICE_FAILURE => 0x04;
  1         1  
  1         45  
63 1     1   5 use constant EXP_ACKNOWLEDGE => 0x05;
  1         2  
  1         46  
64 1     1   5 use constant EXP_SLAVE_DEVICE_BUSY => 0x06;
  1         1  
  1         34  
65 1     1   3 use constant EXP_MEMORY_PARITY_ERROR => 0x08;
  1         2  
  1         39  
66 1     1   5 use constant EXP_GATEWAY_PATH_UNAVAILABLE => 0x0A;
  1         1  
  1         40  
67 1     1   6 use constant EXP_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND => 0x0B;
  1         1  
  1         36  
68             ## Module error codes
69 1     1   4 use constant MB_NO_ERR => 0;
  1         1  
  1         40  
70 1     1   5 use constant MB_RESOLVE_ERR => 1;
  1         1  
  1         34  
71 1     1   4 use constant MB_CONNECT_ERR => 2;
  1         16  
  1         51  
72 1     1   5 use constant MB_SEND_ERR => 3;
  1         1  
  1         33  
73 1     1   4 use constant MB_RECV_ERR => 4;
  1         1  
  1         38  
74 1     1   5 use constant MB_TIMEOUT_ERR => 5;
  1         2  
  1         32  
75 1     1   4 use constant MB_FRAME_ERR => 6;
  1         1  
  1         41  
76 1     1   5 use constant MB_EXCEPT_ERR => 7;
  1         1  
  1         32  
77 1     1   4 use constant MB_CRC_ERR => 8;
  1         2  
  1         2675  
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             ## Get or set read timeout.
200             ##
201              
202             sub timeout {
203 0     0 1   my $self = shift;
204 0           my $timeout = shift;
205             # return timeout if no arg
206 0 0         return $self->{TIMEOUT} unless defined $timeout;
207             # set timeout and return timeout
208 0 0 0       if (($timeout =~ m/^\d{1,5}$/) and
209             ($timeout < 180)) {
210 0           $self->{TIMEOUT} = $timeout;
211             }
212 0           return $self->{TIMEOUT};
213             }
214              
215             ##
216             ## Open TCP link.
217             ##
218              
219             sub open {
220 0     0 1   my $self = shift;
221 0 0         print 'call open()'."\n" if ($self->{debug});
222             # restart TCP if already open
223 0 0         $self->close if ($self->is_open);
224             # name resolve
225 0           my $ad_ip = inet_aton($self->{HOST});
226 0 0         unless($ad_ip) {
227 0           $self->{LAST_ERROR} = MB_RESOLVE_ERR;
228 0 0         print 'IP resolve error'."\n" if ($self->{debug});
229 0           return undef;
230             }
231             # set socket
232 0           socket($self->{sock}, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
233 0           my $connect_ok = connect($self->{sock}, sockaddr_in($self->{PORT}, $ad_ip));
234 0 0         if ($connect_ok) {
235 0           return 1;
236             } else {
237 0           $self->{sock} = undef;
238 0           $self->{LAST_ERROR} = MB_CONNECT_ERR;
239 0 0         print 'TCP connect error'."\n" if ($self->{debug});
240 0           return undef;
241             }
242             };
243              
244             ##
245             ## Check TCP link.
246             ##
247              
248             sub is_open {
249 0     0 1   my $self = shift;
250 0           return (defined $self->{sock});
251             };
252              
253             ##
254             ## Close TCP link.
255             ##
256              
257             sub close {
258 0     0 1   my $self = shift;
259 0 0         if ($self->{sock}) {
260 0           close $self->{sock};
261 0           $self->{sock} = undef;
262 0           return 1;
263             } else {
264 0           return undef;
265             }
266             };
267              
268             ##
269             ## Modbus function READ_COILS (0x01).
270             ## read_coils(bit_addr, bit_number)
271             ## return a ref to result array
272             ## or undef if error
273              
274             sub read_coils {
275 0     0 1   my $self = shift;
276 0           my $bit_addr = shift;
277 0           my $bit_nb = shift;
278             # build frame
279 0           my $tx_buffer = $self->_mbus_frame(READ_COILS, pack("nn", $bit_addr, $bit_nb));
280             # send request
281 0           my $s_send = $self->_send_mbus($tx_buffer);
282             # check error
283 0 0         return undef unless ($s_send);
284             # receive
285 0           my $f_body = $self->_recv_mbus();
286             # check error
287 0 0         return undef unless ($f_body);
288             # register extract
289 0           my ($rx_byte_count, $f_bits) = unpack 'Cb*', $f_body;
290             # read bit(s) string
291 0           my @bits = split //, $f_bits;
292 0           $#bits = $bit_nb - 1;
293 0           return \@bits;
294             }
295              
296             ##
297             ## Modbus function READ_DISCRETE_INPUTS (0x02).
298             ## read_discrete_inputs(bit_addr, bit_number)
299             ## return a ref to result array
300             ## or undef if error
301              
302             sub read_discrete_inputs {
303 0     0 1   my $self = shift;
304 0           my $bit_addr = shift;
305 0           my $bit_nb = shift;
306             # build frame
307 0           my $tx_buffer = $self->_mbus_frame(READ_DISCRETE_INPUTS, pack("nn", $bit_addr, $bit_nb));
308             # send request
309 0           my $s_send = $self->_send_mbus($tx_buffer);
310             # check error
311 0 0         return undef unless ($s_send);
312             # receive
313 0           my $f_body = $self->_recv_mbus();
314             # check error
315 0 0         return undef unless ($f_body);
316             # register extract
317 0           my ($rx_byte_count, $f_bits) = unpack 'Cb*', $f_body;
318             # read bit(s) string
319 0           my @bits = split //, $f_bits;
320 0           $#bits = $bit_nb - 1;
321 0           return \@bits;
322             }
323              
324              
325             ##
326             ## Modbus function READ_HOLDING_REGISTERS (0x03).
327             ## read_holding_registers(reg_addr, reg_number)
328             ## return a ref to result array
329             ## or undef if error
330              
331             sub read_holding_registers {
332 0     0 1   my $self = shift;
333 0           my $reg_addr = shift;
334 0           my $reg_nb = shift;
335             # build frame
336 0           my $tx_buffer = $self->_mbus_frame(READ_HOLDING_REGISTERS, pack("nn", $reg_addr, $reg_nb));
337             # send request
338 0           my $s_send = $self->_send_mbus($tx_buffer);
339             # check error
340 0 0         return undef unless ($s_send);
341             # receive
342 0           my $f_body = $self->_recv_mbus();
343             # check error
344 0 0         return undef unless ($f_body);
345             # register extract
346 0           my ($rx_reg_count, $f_regs) = unpack 'Ca*', $f_body;
347             # read 16 bits register
348 0           my @registers = unpack 'n*', $f_regs;
349 0           return \@registers;
350             }
351              
352             ##
353             ## Modbus function READ_INPUT_REGISTERS (0x04).
354             ## read_input_registers(reg_addr, reg_number)
355             ## return a ref to result array
356             ## or undef if error
357              
358             sub read_input_registers {
359 0     0 1   my $self = shift;
360 0           my $reg_addr = shift;
361 0           my $reg_nb = shift;
362             # build frame
363 0           my $tx_buffer = $self->_mbus_frame(READ_INPUT_REGISTERS, pack("nn", $reg_addr, $reg_nb));
364             # send request
365 0           my $s_send = $self->_send_mbus($tx_buffer);
366             # check error
367 0 0         return undef unless ($s_send);
368             # receive
369 0           my $f_body = $self->_recv_mbus();
370             # check error
371 0 0         return undef unless ($f_body);
372             # register extract
373 0           my ($rx_reg_count, $f_regs) = unpack 'Ca*', $f_body;
374             # read 16 bits register
375 0           my @registers = unpack 'n*', $f_regs;
376 0           return \@registers;
377             }
378              
379             ##
380             ## Modbus function WRITE_SINGLE_COIL (0x05).
381             ## write_single_coil(bit_addr, bit_value)
382             ## return 1 if write success
383             ## or undef if error
384              
385             sub write_single_coil {
386 0     0 1   my $self = shift;
387 0           my $bit_addr = shift;
388 0           my $bit_value = shift;
389             # build frame
390 0 0         $bit_value = ($bit_value) ? 0xFF : 0;
391 0           my $tx_buffer = $self->_mbus_frame(WRITE_SINGLE_COIL, pack("nCC", $bit_addr, $bit_value, 0));
392             # send request
393 0           my $s_send = $self->_send_mbus($tx_buffer);
394             # check error
395 0 0         return undef unless ($s_send);
396             # receive
397 0           my $f_body = $self->_recv_mbus();
398             # check error
399 0 0         return undef unless ($f_body);
400             # register extract
401 0           my ($rx_bit_addr, $rx_bit_value, $rx_padding) = unpack 'nCC', $f_body;
402             # check bit write
403 0 0 0       return (($rx_bit_addr == $bit_addr) and ($rx_bit_value == $bit_value)) ? 1 : undef;
404             }
405              
406             ##
407             ## Modbus function WRITE_SINGLE_REGISTER (0x06).
408             ## write_single_register(reg_addr, reg_value)
409             ## return 1 if write success
410             ## or undef if error
411              
412             sub write_single_register {
413 0     0 1   my $self = shift;
414 0           my $reg_addr = shift;
415 0           my $reg_value = shift;
416             # build frame
417 0           my $tx_buffer = $self->_mbus_frame(WRITE_SINGLE_REGISTER, pack("nn", $reg_addr, $reg_value));
418             # send request
419 0           my $s_send = $self->_send_mbus($tx_buffer);
420             # check error
421 0 0         return undef unless ($s_send);
422             # receive
423 0           my $f_body = $self->_recv_mbus();
424             # check error
425 0 0         return undef unless ($f_body);
426             # register extract
427 0           my ($rx_reg_addr, $rx_reg_value) = unpack 'nn', $f_body;
428             # check bit write
429 0 0 0       return (($rx_reg_addr == $reg_addr) and ($rx_reg_value == $reg_value)) ? 1 : undef;
430             }
431              
432             ##
433             ## Modbus function WRITE_MULTIPLE_REGISTERS (0x10).
434             ## write_multiple_registers(reg_addr, ref_to_reg_array)
435             ## return 1 if write success
436             ## or undef if error
437              
438             sub write_multiple_registers {
439 0     0 1   my $self = shift;
440 0           my $reg_addr = shift;
441 0           my $ref_array_reg = shift;
442 0           my @reg_value = @$ref_array_reg;
443             # build frame
444             # register
445 0           my $reg_nb = @reg_value;
446             # format reg value string
447 0           my $reg_val_str;
448 0           for (@reg_value) {$reg_val_str .= pack("n", $_);}
  0            
449 0           my $bytes_nb = bytes::length($reg_val_str);
450             # format modbus frame body
451 0           my $body = pack("nnC", $reg_addr, $reg_nb, $bytes_nb).$reg_val_str;
452 0           my $tx_buffer = $self->_mbus_frame(WRITE_MULTIPLE_REGISTERS, $body);
453             # send request
454 0           my $s_send = $self->_send_mbus($tx_buffer);
455             # check error
456 0 0         return undef unless ($s_send);
457             # receive
458 0           my $f_body = $self->_recv_mbus();
459             # check error
460 0 0         return undef unless ($f_body);
461             # register extract
462 0           my ($rx_reg_addr, $rx_reg_nb) = unpack 'nn', $f_body;
463             # check regs write
464 0 0         return ($rx_reg_addr == $reg_addr) ? 1 : undef;
465             }
466              
467             # Build modbus frame.
468             # _mbus_frame(function code, body)
469             # return modbus frame
470             sub _mbus_frame {
471 0     0     my $self = shift;
472 0           my $fc = shift;
473 0           my $body = shift;
474             # build frame body
475 0           my $f_body = pack("C", $fc).$body;
476             # modbus/TCP
477 0 0         if ($self->{MODE} == MODBUS_TCP) {
    0          
478             # build frame ModBus Application Protocol header (mbap)
479 0           $self->{hd_tr_id} = int(rand 65535);
480 0           my $tx_hd_pr_id = 0;
481 0           my $tx_hd_length = bytes::length($f_body) + 1;
482             my $f_mbap = pack("nnnC", $self->{hd_tr_id}, $tx_hd_pr_id,
483 0           $tx_hd_length, $self->{UNIT_ID});
484 0           return $f_mbap.$f_body;
485             # modbus RTU
486             } elsif ($self->{MODE} == MODBUS_RTU) {
487             # format [slave addr(unit_id)]frame_body[CRC16]
488 0           my $slave_ad = pack("C", $self->{UNIT_ID});
489 0           return $self->_add_crc($slave_ad.$f_body);
490             } else {
491             # unknow mode
492 0           return undef;
493             }
494             }
495              
496             # Send modbus frame.
497             # _send_mbus(frame)
498             # return $nb_byte send
499             sub _send_mbus {
500 0     0     my $self = shift;
501 0           my $frame = shift;
502             # send request
503 0           my $bytes_send = $self->_send($frame);
504 0 0         return undef unless ($bytes_send);
505             # for debug
506 0 0         $self->_pretty_dump('Tx', $frame) if ($self->{debug});
507             # return
508 0           return $bytes_send;
509             }
510              
511             # Recv modbus frame.
512             # _recv_mbus()
513             # return body (after func. code)
514             sub _recv_mbus {
515 0     0     my $self = shift;
516             ## receive
517             # vars
518 0           my ($rx_buffer,$rx_frame);
519 0           my ($rx_unit_id, $rx_bd_fc, $f_body);
520             # modbus TCP receive
521 0 0         if ($self->{MODE} == MODBUS_TCP) {
    0          
522             # 7 bytes head
523 0           $rx_buffer = $self->_recv(7);
524 0 0         return undef unless($rx_buffer);
525 0           $rx_frame = $rx_buffer;
526             # decode
527 0           my ($rx_hd_tr_id, $rx_hd_pr_id, $rx_hd_length, $rx_hd_unit_id) = unpack "nnnC", $rx_frame;
528             # check
529 0 0 0       if (!(($rx_hd_tr_id == $self->{hd_tr_id}) && ($rx_hd_pr_id == 0) &&
      0        
      0        
530             ($rx_hd_length < 256) && ($rx_hd_unit_id == $self->{UNIT_ID}))) {
531 0           $self->close;
532 0           return undef;
533             }
534             # end of frame
535 0           $rx_buffer = $self->_recv($rx_hd_length-1);
536 0 0         return undef unless($rx_buffer);
537 0           $rx_frame .= $rx_buffer;
538             # dump frame
539 0 0         $self->_pretty_dump('Rx', $rx_frame) if ($self->{debug});
540             # body decode
541 0           ($rx_bd_fc, $f_body) = unpack "Ca*", $rx_buffer;
542             # modbus RTU receive
543             } elsif ($self->{MODE} == MODBUS_RTU) {
544 0           $rx_buffer = $self->_recv(FRAME_RTU_MAXSIZE);
545 0 0         return undef unless($rx_buffer);
546 0           $rx_frame = $rx_buffer;
547             # dump frame
548 0 0         $self->_pretty_dump('Rx', $rx_frame) if ($self->{debug});
549             # RTU frame min size is 5: check this here
550 0 0         if (bytes::length($rx_frame) < 5) {
551 0           $self->{LAST_ERROR} = MB_RECV_ERR;
552 0 0         print 'short frame error'."\n" if ($self->{debug});
553 0           $self->close;
554 0           return undef;
555             }
556             # check CRC
557 0 0         if (! $self->_crc_is_ok($rx_frame)) {
558 0           $self->{LAST_ERROR} = MB_CRC_ERR;
559 0 0         print 'CRC error'."\n" if ($self->{debug});
560 0           $self->close;
561 0           return undef;
562             }
563             # remove CRC
564 0           $rx_frame = bytes::substr($rx_frame, 0, -2);
565             # body decode
566 0           ($rx_unit_id, $rx_bd_fc, $f_body) = unpack "CCa*", $rx_frame;
567             # check
568 0 0         if (!($rx_unit_id == $self->{UNIT_ID})) {
569 0           $self->close;
570 0           return undef;
571             }
572             }
573             # check except
574 0 0         if ($rx_bd_fc > 0x80) {
575             # except code
576 0           my ($exp_code) = unpack "C", $f_body;
577 0           $self->{LAST_ERROR} = MB_EXCEPT_ERR;
578 0           $self->{LAST_EXCEPT} = $exp_code;
579 0 0         print 'except (code '.$exp_code.')'."\n" if ($self->{debug});
580 0           return undef;
581             } else {
582             # return
583 0           return $f_body;
584             }
585             }
586              
587             # Send data over current socket.
588             # _send(data_to_send)
589             # return the number of bytes send
590             # or undef if error
591             sub _send {
592 0     0     my $self = shift;
593 0           my $data = shift;
594             # check link, open if need
595 0 0         unless ($self->is_open) {
596 0 0         print 'call _send() not open -> call open()'."\n" if ($self->{debug});
597 0 0         return undef unless ($self->open);
598             }
599             # send data
600 0           my $data_l = bytes::length($data);
601 0           my $send_l = send($self->{sock}, $data, 0);
602             # send error
603 0 0         if ($send_l != $data_l) {
604 0           $self->{LAST_ERROR} = MB_SEND_ERR;
605 0 0         print '_send error'."\n" if ($self->{debug});
606 0           $self->close;
607 0           return undef;
608             } else {
609 0           return $send_l;
610             }
611             }
612              
613             # Recv data over current socket.
614             # _recv(max_size)
615             # return the receive buffer
616             # or undef if error
617             sub _recv {
618 0     0     my $self = shift;
619 0           my $max_size = shift;
620             # wait for read
621 0 0         unless ($self->_can_read()) {
622 0           $self->close;
623 0           return undef;
624             }
625             # recv
626 0           my $buffer;
627 0           my $s_recv = recv($self->{sock}, $buffer, $max_size, 0);
628 0 0         unless (defined $s_recv) {
629 0           $self->{LAST_ERROR} = MB_RECV_ERR;
630 0 0         print '_recv error'."\n" if ($self->{debug});
631 0           $self->close;
632 0           return undef;
633             }
634 0           return $buffer;
635             }
636              
637             # Wait for socket read.
638             sub _can_read {
639 0     0     my $self = shift;
640 0           my $hdl_select = "";
641 0           vec($hdl_select, fileno($self->{sock}), 1) = 1;
642 0           my $_select = select($hdl_select, undef, undef, $self->{TIMEOUT});
643 0 0         if ($_select) {
644 0           return $_select;
645             } else {
646 0           $self->{LAST_ERROR} = MB_TIMEOUT_ERR;
647 0 0         print 'timeout error'."\n" if ($self->{debug});
648 0           $self->close;
649 0           return undef;
650             }
651             }
652              
653             # Compute modbus CRC16 (for RTU mode).
654             # _crc(modbus_frame)
655             # return the CRC
656             sub _crc {
657 0     0     my $self = shift;
658 0           my $frame = shift;
659 0           my $crc = 0xFFFF;
660 0           my ($chr, $lsb);
661 0           for my $i (0..bytes::length($frame)-1) {
662 0           $chr = ord(bytes::substr($frame, $i, 1));
663 0           $crc ^= $chr;
664 0           for (1..8) {
665 0           $lsb = $crc & 1;
666 0           $crc >>= 1;
667 0 0         $crc ^= 0xA001 if $lsb;
668             }
669             }
670 0           return $crc;
671             }
672              
673             # Add CRC to modbus frame (for RTU mode).
674             # _add_crc(modbus_frame)
675             # return modbus_frame_with_crc
676             sub _add_crc {
677 0     0     my $self = shift;
678 0           my $frame = shift;
679 0           my $crc = pack 'v', $self->_crc($frame);
680 0           return $frame.$crc;
681             }
682              
683             # Check the CRC of modbus RTU frame.
684             # _crc_is_ok(modbus_frame_with_crc)
685             # return true if CRC is ok
686             sub _crc_is_ok {
687 0     0     my $self = shift;
688 0           my $frame = shift;
689 0           return ($self->_crc($frame) == 0);
690             }
691              
692             # Print modbus/TCP frame ("[header]body") or modbus RTU ("body[CRC]").
693             sub _pretty_dump {
694 0     0     my $self = shift;
695 0           my $label = shift;
696 0           my $data = shift;
697 0           my @dump = map {sprintf "%02X", $_ } unpack("C*", $data);
  0            
698             # format for TCP or RTU
699 0 0         if ($self->{MODE} == MODBUS_TCP) {
    0          
700 0           $dump[0] = "[".$dump[0];
701 0           $dump[5] = $dump[5]."]";
702             } elsif ($self->{MODE} == MODBUS_RTU) {
703 0           $dump[$#dump-1] = "[".$dump[$#dump-1];
704 0           $dump[$#dump] = $dump[$#dump]."]";
705             }
706             # print result
707 0           print $label."\n";
708 0           for (@dump) {print $_." ";}
  0            
709 0           print "\n\n";
710             }
711              
712             1;
713              
714             __END__