EAFDSS
view release on metacpan or search on metacpan
lib/EAFDSS/SDNP.pm view on Meta::CPAN
=head2 init
The constructor
=cut
sub init {
my($class) = shift @_;
my($config) = @_;
my($self) = $class->SUPER::init(@_);
$self->debug("Initializing");
if (! exists $config->{PARAMS}) {
return $self->error("No parameters have been given!");
} else {
$self->{IP} = $config->{PARAMS};
$self->{PORT} = 24222;
}
$self->debug(" Socket Initialization to IP/hostname [%s]", $self->{IP});
$self->{_SOCKET} = IO::Socket::INET->new(PeerPort => $self->{PORT}, Proto => 'udp', PeerAddr => $self->{IP});
if (! defined $self->{_SOCKET}) {
return undef;
}
$self->debug(" Setting timers");
$self->_setTimer('_TSYNC', 0);
$self->_setTimer('_T0', 0);
$self->_setTimer('_T1', 0);
$self->debug(" Setting frame counter to 1");
$self->{_FSN} = 1;
my($reply, $deviceID) = $self->PROTO_ReadDeviceID();
$self->debug(" Read device ID");
if ( ($reply == 0) && ($deviceID ne $self->{SN}) ) {
return $self->error("Serial Number not matching");
}
$self->debug(" Init OK");
return $self;
}
=head2 SendRequest
The ethernet version of SendRequest command.
=cut
sub SendRequest {
my($self) = shift @_;
my($opcode) = shift @_;
my($opdata) = shift @_;
my($data) = shift @_;
my(%reply) = ();
# For at least 6 times do:
my($busy_try, $state, $try);
BUSY: for ($busy_try = 1; $busy_try <= 3; $busy_try++) {
for ($try = 1; $try < 6; $try++) {
my(%reply) = ();
$self->debug(" Send Request try #%d", $try);
SYNC:
# If state is UNSYNCHRONIZED or connection SYNC timer expired then:
if ($self->_getTimer('_TSYNC') >= 0) {
$self->debug(" Syncing with device");
if ( $self->_sdnpSync() == 0) {
$self->debug(" Sync Failed");
$self->error(64+3);
return %reply;
}
}
SEND:
# Send REQUEST(Connection's NextFSN) using 'RequestDataPacket';
my($msg) = $self->_sdnpPacket($opcode, $opdata, $data);
$self->_sdnpPrintFrame(" ----> [%s]", $msg);
$self->{_SOCKET}->send($msg);
# Set T0 timer to 800 milliseconds;
$self->_setTimer('_T0', 0.800);
# Do until T0 expires:
while ($self->_getTimer('_T0') <= 0) {
my($frame) = undef;
$self->{_SOCKET}->recv($frame, 512);
if ($frame) {
%reply = $self->_sdnpAnalyzeFrame($frame);
$self->_sdnpPrintFrame(" <---- [%s]", $msg);
$reply{'HOST'} = $self->{_SOCKET}->peerhost();
} else {
$reply{HOST} = -1;
}
# If a valid SDNP frame received then do
if ($self->_sdnpFrameCheck(\%reply)) {
# If received frame's FSN <> Request frame's FSN
if ($self->{_FSN} != $reply{SN}) {
$self->debug(" Bad FSN, Discarding\n");
next;
} else {
# Test received frame's opcode;
# Case RST:
if ($reply{OPCODE} == 0x10) {
# Set connection's state to UNSYNCHRONIZED;
$self->_setTimer('_TSYNC', 0);
goto SYNC;
}
# Case NAK:
if ($reply{OPCODE} == 0x13) {
goto SEND;
}
# Case REPLY:
if ($reply{OPCODE} == 0x22) {
# If received frame's data packet does not validate okay then:
my($i, $checksum) = (0, 0xAA55);
for ($i=0; $i < length($reply{DATA}); $i++) {
$checksum += ord substr($reply{DATA}, $i, 1);
}
#$self->debug( " Checking Data checksum [%04X]", $checksum);
if ($checksum != $reply{CHECKSUM}) {
# Create and send NAK frame with FSN set to received FSN;
my($msg) = $self->sdnpPacket(0x13, 0x00);
$self->_sdnpPrintFrame(" ----> [%s]\n", $msg);
$self->{_SOCKET}->send($msg);
next;
} else {
if ( $reply{DATA} =~ /^0E/ ) {
$self->debug(" Will retry because of busyness $busy_try");
$state = "BUSY";
sleep 2;
next BUSY;
} else {
$self->debug(" Done Getting reply");
# Renew connection's SYNC timer;
$self->_setTimer('_TSYNC', 4);
# Advance connection's NextFSN by one;
$self->{_FSN}++;
# Return request transmittion success;
return %reply;
}
}
}
$self->debug( " Bad Frame, Discarding");
next;
}
} else {
$self->debug( " Bad Frame, Discarding");
}
}
}
}
if ($state eq "BUSY") {
$self->debug(" Too busy device... aborting");
$reply{DATA} = "0E/0/";
}
# Return request transmittion failure;
return %reply;
}
sub _sdnpQuery {
my($self) = shift @_;
my($devices);
$self->_sdnpSendQuery();
# Set timer T0 to 500 milliseconds;
$self->_setTimer('_T0', 0.500);
# Do until T0 expires:
while ($self->_getTimer('_T0') <= 0) {
my(%reply) = ();
my($frame) = undef;
my($query_socket) = IO::Socket::INET->new(
LocalPort => $self->{PORT} + 1,
Proto => 'udp',
);
if (! defined $query_socket) {
return undef;
}
$query_socket->recv($frame, 512);
if ($frame) {
%reply = $self->_sdnpAnalyzeFrame($frame);
$self->_sdnpPrintFrame(" <---- [%s]", $frame);
$reply{'HOST'} = $query_socket->peerhost();
# If a valid frame received then:
if ($self->_sdnpQueryFrameCheck(\%reply)) {
#If frame type is ACTIVE then:
if ($reply{OPCODE} == 0x01) {
#If ACTIVE(FSN) = QUERY(FSN) then:
if ($self->{_FSN} == $reply{SN}) {
#Add IP address of sender to device list;
$self->debug(" Found host on IP %s", $reply{'HOST'});
$devices->{$reply{'HOST'}} = 1;
}
}
}
}
close($query_socket);
$self->_setTimer('_T1', 0.500);
if ($self->_getTimer('_T1') >= 0) {
$self->_sdnpSendQuery();
$self->_setTimer('_T1', 0.500);
}
}
$self->_setTimer('_TSYNC', 0);
( run in 1.735 second using v1.01-cache-2.11-cpan-39bf76dae61 )