BACnet
view release on metacpan or search on metacpan
lib/BACnet/Socket.pm view on Meta::CPAN
#!/usr/bin/perl
package BACnet::Socket;
use v5.16;
use Data::Dumper;
use Future::AsyncAwait;
use IO::Async::Socket;
use IO::Async::Loop;
use Socket qw(unpack_sockaddr_in inet_ntoa pack_sockaddr_in inet_aton);
use BACnet::Device;
use Scalar::Util 'weaken';
my $BACNET_PORT = 0xBAC0; # 47808
sub new {
my ( $class, $device, %args ) = @_;
weaken { $device };
my $self = {
retries => $args{retries} // 3,
timeout => $args{timeout} // 3,
loop => $args{io_loop} // IO::Async::Loop->new,
debug => $args{debug},
stime => time, # for debugging timestamps
device => $device,
locks => {},
};
bless $self, $class;
$self->{sock} =
IO::Async::Socket->new( on_recv => sub { $self->_recv(@_) }, );
$self->loop->add( $self->{sock} );
$self->sock->bind(
socktype => 'dgram',
addr => $args{addr},
service => $args{sport} // 0,
)->get; # local bind() does not block
return $self;
}
sub _stop {
my ($self) = @_;
$self->loop->delay_future( after => 2, )->on_done(
sub {
$self->loop->stop;
}
);
}
sub _debug {
my ( $self, @msg ) = @_;
return if !$self->{debug};
say STDERR sprintf( "+%04ds", time - $self->{stime} ), ' ', @msg;
}
sub _recv {
my ( $self, $sock, $dgram, $addr ) = @_;
my ( $port, $ip ) = unpack_sockaddr_in($addr);
my $ipaddr = inet_ntoa($ip);
$self->_debug("got packet from $ipaddr:$port");
my $packet = BACnet::BVLC->parse($dgram);
$self->_debug( join( ' ', '< recv', unpack( "(H2)*", $dgram ) ) );
if (
defined $packet->{payload}
&& defined $packet->{payload}->{invoke_id}
&& defined $self->{reader_of}
{ $addr . ':' . $packet->{payload}{invoke_id} }
&& _is_response(
$self->{reader_of}{ $addr . ':' . $packet->{payload}{invoke_id} }
( run in 1.083 second using v1.01-cache-2.11-cpan-39bf76dae61 )