=head1 NAME

MBclient - Add modbus TCP or RTU functions for your program.

=head1 SYNOPSIS

  use strict;
  use MBclient;

  my $m = MBclient->new();
  # define server target
  $m->host("localhost");
  $m->unit_id(1);

  # read 16 bits register from ad 0 to 9
  my $words = $m->read_holding_registers(0, 10);

  # print words
  foreach my $word (@$words) {
    print $word."\n";
  }

  # clean exit
  $m->close();
  exit 0;

=head1 DESCRIPTION

Modbus is a standard serial communication protocol used to interconnect industrial
PLC (and a lot of other things). This module gives you access to TCP and RTU
version of this protocol, through the MBclient object.

You can have a look at http://en.wikipedia.org/wiki/Modbus for details.

=head1 INSTALL

You can install this module from:

CPAN, the easy way:

    sudo perl -MCPAN -e'install MBclient'

GitHub:

    git clone https://github.com/sourceperl/MBclient.git
    cd MBclient
    perl Makefile.PL
    make
    make test
    sudo make install

=head1 DEPENDENCIES

This module requires no other module or librarie.

It's pure Perl code without any extension.

=head1 USAGE

=head2 How you might use this

Here is how you I<might> use the MBclient module.

First you have to create the object, and to set main params : host, port and unit_id.

Then call open() function, or directly a "modbus function". In any case, you have to call the close() method, in order
to cleanly close the TCP link.

=head2 Functions

The following functions are defined in the MBclient module. Most of
them are I<public>, meaning that you're supposed to use
them. Some are I<private>, meaning that you're not supposed to use
them. Assume that functions are I<public>
unless otherwise documented.

=over

=item new()

new() create an MBclient object.

Example: C<my $m = MBclient-E<gt>new();>

=item open()

open() funtion open the TCP link. If the TCP link is up when the function is
called a close/open cycle is initiated.

This function return undef in case of error.

Example: C<$m-E<gt>open();>

=item is_open()

is_open() return True if TCP link is open, False when TCP is closed

Example: C<$m-E<gt>is_open();>

=item close()

close() function close the TCP link.

Return True if success, undef if error.

Example: C<$m-E<gt>close();>

=item mode(MODBUS_MODE)

Use to set modbus mode : TCP (default value) or RTU (add crc16 on every frame).

2 constants are import with this module MODBUS_TCP and MODBUS_RTU. Use it to
define modbus mode.

Example: C<$m-E<gt>mode(MODBUS_RTU);>

=item timeout(TIMEOUT)

Use to set modbus read timeout : 30 default value.

Timeout defines how long will read wait until it exits and aborts communication.

Example: C<$m-E<gt>timeout(5);>

=item host(hostname)

Use to set server IPv4 like "192.168.0.1" or URL name like "plc1.domain.net".

Return "hostname" if success, undef in other case.

Example: C<$m-E<gt>host("192.168.0.1");>

You can read hostname property if you call host() without arg.

=item port(port)

Use to set server port (default is 502).

Return "port" if success, undef in other case.

Example: C<$m-E<gt>port(888);>

You can read port property if you call port() without arg.

=item unit_id(uid)

Use to set server modbus unit id (default is 1).

Return "uid" if success, undef in other case.

Example: C<$m-E<gt>unit_id(1);>

You can read port property if you call unit_id() without arg.

=item last_error()

last_error() return last error on MBclient object.

See source code for "module error code" list.

Each error is a constant import in your program, so you can do thing like:

  if ($m->last_error() == MB_RESOLVE_ERR) {
    print "unable to resolve name\n";
    exit 2;
  }

=item last_except()

last_except() return last modbus exception code.

See source code for "modbus except code" list.

Each except is a constant import in your program, so you can do thing like:

  if ($m->last_except() == EXP_GATEWAY_PATH_UNAVAILABLE) {
    print "check your modbus gateway please\n";
    exit 3;
  }

=item version()

version() return current version.

Example: C<print $m-E<gt>version();>

=item read_coils(bit_addr, bit_nb)

read_coils() is standard modbus function 1.

This funtion read "bit_nb" number of bits at "bit_addr" bit address.

Return a ref to a bits array or undef if error.

Example read 10 bits at address 55:

  my $bits = $m->read_coils(55, 10);
  foreach my $bit (@$bits) {
    print $bit."\n";
  }

=item read_discrete_inputs(bit_addr, bit_nb)

read_discrete_inputs() is standard modbus function 2.

This funtion read "bit_nb" number of bits at "bit_addr" bit address.

Return a ref to a bits array or undef if error.

Example read 1 bit at hex address 45:

  my $bits = $m->read_discrete_inputs(0x45, 1);
  if ($bits) {
    print $$bits[0]."\n";
  } else {
    print "error code: ".$m->last_error()."\n";
  }

=item read_holding_registers(reg_addr, reg_nb)

read_holding_registers() is standard modbus function 3.

This funtion read "reg_nb" number of registers at "reg_addr" register address.

Return a ref to a registers array or undef if error.

Example read 2 registers at hex address 66:

  my $regs = $m->read_holding_registers(0x66, 2);
  foreach my $reg (@$regs) {
    print $reg."\n";
  }

=item read_input_registers(reg_addr, reg_nb)

read_input_registers() is standard modbus function 4.

This funtion read "reg_nb" number of registers at "reg_addr" register address.

Return a ref to a registers array or undef if error.

Example read 4 registers at hex address 100:

  my $regs = $m->read_input_registers(0x100, 4);
  foreach my $reg (@$regs) {
    print $reg."\n";
  }

=item write_single_coil(bit_addr, bit_value)

write_single_coil() is standard modbus function 5.

This funtion write "bit_value" (0 or 1) to "bit_addr" bit address.

Return True if write success, undef if error.

Example write 1 on bit at address 45:

  if ($m->write_single_coil(45, 1)) {
    print "write success\n";
  } else {
    print "write error\n";
  }


=item write_single_register(reg_addr, reg_value)

write_single_register() is standard modbus function 6.

This funtion write "reg_value" register value to "reg_addr" register address.

Return True if write success, undef if error.

Example write 578 on register at address 55:

  if ($m->write_single_register(55, 578)) {
    print "write success\n";
  } else {
    print "write error\n";
  }


=item write_multiple_registers(reg_addr, ref_array_reg)

write_multiple_registers() is standard modbus function 16.

This funtion write an array of register with reference to this array in
"ref_array_reg" at "reg_addr" register address.

Return True if write success, undef if error.

Example write 3 registers at address 780:

  my @to_write = (45, 22, 33);
  if ($m->write_multiple_registers(780, \@to_write)) {
    print "write success\n";
  } else {
    print "write error\n";
  }

=item _mbus_frame(fc, body)

B<PRIVATE>

Build the modbus frame. Called with "fc" as function code and "body" as modbus
body frame.

Return the modbus frame or undef if error.

=item _send_mbus(frame)

B<PRIVATE>

Send "frame" over the current socket with debug pretty print option.

Return the number of byte send or undef if error.

=item _recv_mbus()

B<PRIVATE>

Receive modbus frame.

Return body of the modbus frame.

=item _send(data)

B<PRIVATE>

Send "data" over the current socket.

Return the number of byte send or undef if error.

=item _recv(max_size)

B<PRIVATE>

Receive "max_size" bytes of data from the current socket.

Return receive buffer or undef if error.

=item _can_read()

B<PRIVATE>

This function wait for data available on socket, block for a max of timeout
second.

=item _crc(frame)

B<PRIVATE>

This function compute crc16 for arg "frame".

Return crc16.

=item _add_crc(frame)

B<PRIVATE>

Return modbus arg "frame" with crc16 at the end.

=item _crc_is_ok(frame)

B<PRIVATE>

Check the crc16 of modbus arg frame.

Return True if crc16 is ok, False in other case.

=item _pretty_dump(label, data)

B<PRIVATE>

Print modbus/TCP frame in pretty format.

=back

=head1 NOTES

- The file "examples/read_10_reg.pl " is provided as a basic script. You can use
it as a skeleton to start writing a modbus script.

- Advance users can enable debug messages with C<$m-E<gt>{debug}=1;>

=head1 BUGS

Thanks to report issues at https://github.com/sourceperl/MBclient/

=head1 AUTHOR

Loic Lefebvre, E<lt>lle@cpan.orgE<gt>

=head1 COPYRIGHT & LICENSE

Copyright 2014 Loic Lefebvre

The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.