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 )