/*----- PROTECTED REGION ID(Socket2.cpp) ENABLED START -----*/
//=============================================================================
//
// file :        Socket2.cpp
//
// description : C++ source for the Socket2 class and its commands.
//               The class is derived from Device. It represents the
//               CORBA servant object which will be accessed from the
//               network. All commands which can be executed on the
//               Socket2 are implemented in this file.
//
// project :     
//
// This file is part of Tango device class.
// 
// Tango is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Tango is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Tango.  If not, see <http://www.gnu.org/licenses/>.
// 
// $Author: alessio $
//
// $Revision: 1.26 $
// $Date: 2019-01-10 15:58:04 $
//
// $HeadURL:  $
//
//=============================================================================
//                This file is generated by POGO
//        (Program Obviously used to Generate tango Object)
//=============================================================================


#include <Socket2.h>
#include <Socket2Class.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <algorithm>
#include <ctime>
#include <csignal>

/*----- PROTECTED REGION END -----*/	//	Socket2.cpp

/**
 *  Socket2 class description:
 *    
 */

//================================================================
//  The following table gives the correspondence
//  between command and method names.
//
//  Command name  |  Method name
//================================================================
//  Write         |  write
//  Read          |  read
//  ReadUntil     |  read_until
//================================================================

//================================================================
//  Attributes managed are:
//================================================================
//  InputLength    |  Tango::DevLong	Scalar
//  OutputLength   |  Tango::DevLong	Scalar
//  Reconnections  |  Tango::DevLong	Scalar
//================================================================

namespace Socket2_ns
{
/*----- PROTECTED REGION ID(Socket2::namespace_starting) ENABLED START -----*/

//	static initializations

/*----- PROTECTED REGION END -----*/	//	Socket2::namespace_starting

//--------------------------------------------------------
/**
 *	Method     : Socket2::Socket2()
 *	Description: Constructors for a Tango device
 *                implementing the classSocket2
 */
//--------------------------------------------------------
Socket2::Socket2(Tango::DeviceClass *cl, std::string &s)
 : TANGO_BASE_CLASS(cl, s.c_str())
{
	/*----- PROTECTED REGION ID(Socket2::constructor_1) ENABLED START -----*/
	init_device();

	/*----- PROTECTED REGION END -----*/	//	Socket2::constructor_1
}
//--------------------------------------------------------
Socket2::Socket2(Tango::DeviceClass *cl, const char *s)
 : TANGO_BASE_CLASS(cl, s)
{
	/*----- PROTECTED REGION ID(Socket2::constructor_2) ENABLED START -----*/
	init_device();
	
	/*----- PROTECTED REGION END -----*/	//	Socket2::constructor_2
}
//--------------------------------------------------------
Socket2::Socket2(Tango::DeviceClass *cl, const char *s, const char *d)
 : TANGO_BASE_CLASS(cl, s, d)
{
	/*----- PROTECTED REGION ID(Socket2::constructor_3) ENABLED START -----*/
	init_device();
	
	/*----- PROTECTED REGION END -----*/	//	Socket2::constructor_3
}
//--------------------------------------------------------
Socket2::~Socket2()
{
	delete_device();
}

//--------------------------------------------------------
/**
 *	Method     : Socket2::delete_device()
 *	Description: will be called at device destruction or at init command
 */
//--------------------------------------------------------
void Socket2::delete_device()
{
	DEBUG_STREAM << "Socket2::delete_device() " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Socket2::delete_device) ENABLED START -----*/
	
	//	Delete device allocated objects
	close();
	
	/*----- PROTECTED REGION END -----*/	//	Socket2::delete_device
	delete[] attr_InputLength_read;
	delete[] attr_OutputLength_read;
	delete[] attr_Reconnections_read;
}

//--------------------------------------------------------
/**
 *	Method     : Socket2::init_device()
 *	Description: will be called at device initialization.
 */
//--------------------------------------------------------
void Socket2::init_device()
{
	DEBUG_STREAM << "Socket2::init_device() create device " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Socket2::init_device_before) ENABLED START -----*/
	
	//	Initialization before get_device_property() call
	reconnections = 0;
	init_error.clear();
	/*----- PROTECTED REGION END -----*/	//	Socket2::init_device_before


	//	Get the device properties from database
	get_device_property();

	attr_InputLength_read = new Tango::DevLong[1];
	attr_OutputLength_read = new Tango::DevLong[1];
	attr_Reconnections_read = new Tango::DevLong[1];
	//	No longer if mandatory property not set.
	if (mandatoryNotDefined)
		return;

	/*----- PROTECTED REGION ID(Socket2::init_device) ENABLED START -----*/

	// Disabling SIGPIPE signal
	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		init_error = "Fail to ignore SIGPIPE signal";

	// Initialize device
	if (init_error.empty()) {
		resolve();
		open();
		check_state(true);
	}
	/*----- PROTECTED REGION END -----*/	//	Socket2::init_device
}

//--------------------------------------------------------
/**
 *	Method     : Socket2::get_device_property()
 *	Description: Read database to initialize property data members.
 */
//--------------------------------------------------------
void Socket2::get_device_property()
{
	/*----- PROTECTED REGION ID(Socket2::get_device_property_before) ENABLED START -----*/
	
	//	Initialize property data members
	
	/*----- PROTECTED REGION END -----*/	//	Socket2::get_device_property_before

	mandatoryNotDefined = false;

	//	Read device properties from database.
	Tango::DbData	dev_prop;
	dev_prop.push_back(Tango::DbDatum("Hostname"));
	dev_prop.push_back(Tango::DbDatum("Port"));
	dev_prop.push_back(Tango::DbDatum("Protocol"));
	dev_prop.push_back(Tango::DbDatum("Timeout"));
	dev_prop.push_back(Tango::DbDatum("IOMultiplexing"));

	//	is there at least one property to be read ?
	if (dev_prop.size()>0)
	{
		//	Call database and extract values
		if (Tango::Util::instance()->_UseDb==true)
			get_db_device()->get_property(dev_prop);

		//	get instance on Socket2Class to get class property
		Tango::DbDatum	def_prop, cl_prop;
		Socket2Class	*ds_class =
			(static_cast<Socket2Class *>(get_device_class()));
		int	i = -1;

		//	Try to initialize Hostname from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  hostname;
		else {
			//	Try to initialize Hostname from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  hostname;
		}
		//	And try to extract Hostname value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  hostname;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize Port from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  port;
		else {
			//	Try to initialize Port from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  port;
		}
		//	And try to extract Port value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  port;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize Protocol from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  protocol;
		else {
			//	Try to initialize Protocol from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  protocol;
		}
		//	And try to extract Protocol value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  protocol;

		//	Try to initialize Timeout from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  timeout;
		else {
			//	Try to initialize Timeout from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  timeout;
		}
		//	And try to extract Timeout value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  timeout;
		//	Property StartDsPath is mandatory, check if has been defined in database.
		check_mandatory_property(cl_prop, dev_prop[i]);

		//	Try to initialize IOMultiplexing from class property
		cl_prop = ds_class->get_class_property(dev_prop[++i].name);
		if (cl_prop.is_empty()==false)	cl_prop  >>  iOMultiplexing;
		else {
			//	Try to initialize IOMultiplexing from default device value
			def_prop = ds_class->get_default_device_property(dev_prop[i].name);
			if (def_prop.is_empty()==false)	def_prop  >>  iOMultiplexing;
		}
		//	And try to extract IOMultiplexing value from database
		if (dev_prop[i].is_empty()==false)	dev_prop[i]  >>  iOMultiplexing;

	}

	/*----- PROTECTED REGION ID(Socket2::get_device_property_after) ENABLED START -----*/
	
	//	Check device property data members init
	transform(iOMultiplexing.begin(), iOMultiplexing.end(), iOMultiplexing.begin(), ::tolower);
	if (iOMultiplexing == "sleep") {
		multiplexing = SLEEP;
	} else {
		multiplexing = SELECT;
	}

	transform(protocol.begin(), protocol.end(), protocol.begin(), ::tolower);
	if (protocol == "udp") {
		proto = UDP;
	} else {
		proto = TCP;
	}

	if (port <= 0 || port > 65535)
		init_error = "Invalid port";

	if (timeout <= 0)
		timeout = 1000;

	DEBUG_STREAM << "Connecting to " << hostname << " on port " << port 
		<< " using " << protocol << " protocol"  << " and " << iOMultiplexing 
		<< " IO multiplexing type"  << " with a timeout of " << timeout << " ms "<< endl;

	timeout_timeval.tv_sec = timeout / 1000;
	timeout_timeval.tv_usec = timeout % 1000 * 1000;

	/*----- PROTECTED REGION END -----*/	//	Socket2::get_device_property_after
}
//--------------------------------------------------------
/**
 *	Method     : Socket2::check_mandatory_property()
 *	Description: For mandatory properties check if defined in database.
 */
//--------------------------------------------------------
void Socket2::check_mandatory_property(Tango::DbDatum &class_prop, Tango::DbDatum &dev_prop)
{
	//	Check if all properties are empty
	if (class_prop.is_empty() && dev_prop.is_empty())
	{
		TangoSys_OMemStream	tms;
		tms << std::endl <<"Property \'" << dev_prop.name;
		if (Tango::Util::instance()->_UseDb==true)
			tms << "\' is mandatory but not defined in database";
		else
			tms << "\' is mandatory but cannot be defined without database";
		append_status(tms.str());
		mandatoryNotDefined = true;
		/*----- PROTECTED REGION ID(Socket2::check_mandatory_property) ENABLED START -----*/
		cerr << tms.str() << " for " << device_name << endl;
		
		/*----- PROTECTED REGION END -----*/	//	Socket2::check_mandatory_property
	}
}


//--------------------------------------------------------
/**
 *	Method     : Socket2::always_executed_hook()
 *	Description: method always executed before any command is executed
 */
//--------------------------------------------------------
void Socket2::always_executed_hook()
{
	DEBUG_STREAM << "Socket2::always_executed_hook()  " << device_name << std::endl;
	if (mandatoryNotDefined)
	{
		Tango::Except::throw_exception(
					(const char *)"PROPERTY_NOT_SET",
					get_status().c_str(),
					(const char *)"Socket2::always_executed_hook()");
	}
	/*----- PROTECTED REGION ID(Socket2::always_executed_hook) ENABLED START -----*/
	
	//	code always executed before all requests
	tout = timeout_timeval;

	if (! init_error.empty()) {
		set_state(Tango::FAULT);
		set_status(init_error);
		DEBUG_STREAM << init_error << endl;
	} else {
		check_state(true);
	}

	/*----- PROTECTED REGION END -----*/	//	Socket2::always_executed_hook
}

//--------------------------------------------------------
/**
 *	Method     : Socket2::read_attr_hardware()
 *	Description: Hardware acquisition for attributes
 */
//--------------------------------------------------------
void Socket2::read_attr_hardware(TANGO_UNUSED(std::vector<long> &attr_list))
{
	DEBUG_STREAM << "Socket2::read_attr_hardware(std::vector<long> &attr_list) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Socket2::read_attr_hardware) ENABLED START -----*/
	
	//	Add your own code
			
	/*----- PROTECTED REGION END -----*/	//	Socket2::read_attr_hardware
}

//--------------------------------------------------------
/**
 *	Read attribute InputLength related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Socket2::read_InputLength(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Socket2::read_InputLength(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Socket2::read_InputLength) ENABLED START -----*/
	//	Set the attribute value
	Tango::AttrQuality qual;
	long len = input_queue_length();
	if (len >= 0) {
		qual = Tango::ATTR_VALID;
		attr_InputLength_read[0] = len + data.size(); 
	} else {
		qual = Tango::ATTR_INVALID;
		attr_InputLength_read[0] = data.size();
	}
	attr.set_value_date_quality(attr_InputLength_read, time(NULL), qual);
	/*----- PROTECTED REGION END -----*/	//	Socket2::read_InputLength
}
//--------------------------------------------------------
/**
 *	Read attribute OutputLength related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Socket2::read_OutputLength(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Socket2::read_OutputLength(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Socket2::read_OutputLength) ENABLED START -----*/
	//	Set the attribute value
	Tango::AttrQuality qual;
	long len = output_queue_length();
	if (len >= 0) {
		qual = Tango::ATTR_VALID;
		attr_OutputLength_read[0] = len;
	}	else {
		qual = Tango::ATTR_INVALID;
		attr_OutputLength_read[0] = 0;
	}
	attr.set_value_date_quality(attr_OutputLength_read, time(NULL), qual);
	/*----- PROTECTED REGION END -----*/	//	Socket2::read_OutputLength
}
//--------------------------------------------------------
/**
 *	Read attribute Reconnections related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Socket2::read_Reconnections(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Socket2::read_Reconnections(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Socket2::read_Reconnections) ENABLED START -----*/
	//	Set the attribute value
	attr_Reconnections_read[0] = reconnections;
	attr.set_value(attr_Reconnections_read);
	
	/*----- PROTECTED REGION END -----*/	//	Socket2::read_Reconnections
}

//--------------------------------------------------------
/**
 *	Method     : Socket2::add_dynamic_attributes()
 *	Description: Create the dynamic attributes if any
 *                for specified device.
 */
//--------------------------------------------------------
void Socket2::add_dynamic_attributes()
{
	/*----- PROTECTED REGION ID(Socket2::add_dynamic_attributes) ENABLED START -----*/
	
	//	Add your own code to create and add dynamic attributes if any
	
	/*----- PROTECTED REGION END -----*/	//	Socket2::add_dynamic_attributes
}

//--------------------------------------------------------
/**
 *	Command Write related method
 *
 *
 *	@param argin
 */
//--------------------------------------------------------
void Socket2::write(const Tango::DevVarCharArray *argin)
{
	DEBUG_STREAM << "Socket2::Write()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Socket2::write) ENABLED START -----*/
	vector<unsigned char> argin_data;
	argin_data << *argin;
	size_t bytes_total = 0, bytes_to_write = argin_data.size();

	if (! init_error.empty()) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", init_error.c_str(), __PRETTY_FUNCTION__);
	}

	if (max(output_queue_length(), 0) != 0) {
		close();
		resolve();
		open();
		reconnections += 1;
		goto error;
	}

	while (bytes_total < bytes_to_write) {
		int s = select(WRITE);
		if (s == 0)
			goto timeout;
		else if (s < 0)
			goto error;
		else { /* s > 0 */ }

		ssize_t bytes_written = _write(
				fd, argin_data.data() + bytes_total,
				bytes_to_write - bytes_total);

		if ( bytes_written > 0) {
			bytes_total += bytes_written;
		} else if (bytes_written == 0) {
			if (multiplexing == SELECT)
				goto error;
			/* Continue if multiplexing == SLEEP */
		} else { /* bytes_written < 0 */
			goto error;
		}
	}

	timeval twait;
	timerclear(&twait);
	twait.tv_usec = 1000;
	while (max(output_queue_length(), 0) != 0) {
		if (! sleep(twait))
			goto timeout;
		timeradd(&twait, &twait, &twait);
	}

	return;

error:
	check_state(false);
	sleep(tout);
timeout:
	Tango::Except::throw_exception(
			"", "Timeout expired", __PRETTY_FUNCTION__);
	/*----- PROTECTED REGION END -----*/	//	Socket2::write
}
//--------------------------------------------------------
/**
 *	Command Read related method
 *
 *
 *	@param argin
 *	@returns
 */
//--------------------------------------------------------
Tango::DevVarCharArray *Socket2::read(Tango::DevLong argin)
{
	Tango::DevVarCharArray *argout;
	DEBUG_STREAM << "Socket2::Read()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Socket2::read) ENABLED START -----*/
	if (! init_error.empty()) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", init_error.c_str(), __PRETTY_FUNCTION__);
	}

	if (argin < 0) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", "Out of limit", __PRETTY_FUNCTION__);
	}

	_read(argin);

	argout = new Tango::DevVarCharArray();
	vector<unsigned char> transfer(data.begin(), data.begin() + argin);
	data.erase(data.begin(), data.begin() + argin);
	*argout << transfer;
	/*----- PROTECTED REGION END -----*/	//	Socket2::read
	return argout;
}
//--------------------------------------------------------
/**
 *	Command ReadUntil related method
 *
 *
 *	@param argin
 *	@returns
 */
//--------------------------------------------------------
Tango::DevVarCharArray *Socket2::read_until(const Tango::DevVarCharArray *argin)
{
	Tango::DevVarCharArray *argout;
	DEBUG_STREAM << "Socket2::ReadUntil()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Socket2::read_until) ENABLED START -----*/
	if (! init_error.empty()) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", init_error.c_str(), __PRETTY_FUNCTION__);
	}

	if (argin->length() != 1) {
		sleep(tout);
		Tango::Except::throw_exception(
				"", "Delimiter has to be exactly one byte", __PRETTY_FUNCTION__);
	}

	char delim = (*argin)[0];
	size_t pos = 0, dsize;
	bool found = false;

	do {
		dsize = data.size();
		for (; pos < dsize; ++pos) {
			if (memcmp(&data[pos], &delim, 1) == 0)	{
				found = true;
				break;
			}
		}
		if (found)
			break;
		_read(dsize + 1);
	} while (true);

	argout = new Tango::DevVarCharArray();
	vector<unsigned char> transfer(data.begin(), data.begin() + pos +1);
	data.erase(data.begin(), data.begin() + pos + 1);
	*argout << transfer;
	/*----- PROTECTED REGION END -----*/	//	Socket2::read_until
	return argout;
}
//--------------------------------------------------------
/**
 *	Method     : Socket2::add_dynamic_commands()
 *	Description: Create the dynamic commands if any
 *                for specified device.
 */
//--------------------------------------------------------
void Socket2::add_dynamic_commands()
{
	/*----- PROTECTED REGION ID(Socket2::add_dynamic_commands) ENABLED START -----*/
	
	//	Add your own code to create and add dynamic commands if any
	
	/*----- PROTECTED REGION END -----*/	//	Socket2::add_dynamic_commands
}

/*----- PROTECTED REGION ID(Socket2::namespace_ending) ENABLED START -----*/

//	Additional Methods
bool Socket2::sleep(timeval tv)
{
	if (! timerisset(&tout))
		return false;

	if (timercmp(&tout, &tv, <)) {
		::sleep(tout.tv_sec);
		usleep(tout.tv_usec);
		timerclear(&tout);
	} else { // tout >= tv
		::sleep(tv.tv_sec);
		usleep(tv.tv_usec);
		timersub(&tout, &tv, &tout);
		assert(tout.tv_sec >= 0 && tout.tv_usec >= 0);
	}
	return true;
}

void Socket2::open()
{
	DEBUG_STREAM << "Opening the file descriptor..." << endl;

	if ((fd = socket(PF_INET, proto == UDP? SOCK_DGRAM:SOCK_STREAM, 0)) == -1) {
		ERROR_STREAM << "Socket creation failed: " 
			<< string(strerror(errno)) << endl;
		return;
	}

	if (proto == TCP) {
		int flag = 1;
		if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, 
					sizeof(flag)) == -1) {
			::close(fd);
			ERROR_STREAM << "Disabling Nagle failed: " 
				<< string(strerror(errno)) << endl;
			return;
		}

		flag = 1;
		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, 
					sizeof(flag)) == -1)	{
			::close(fd);
			ERROR_STREAM << "Enabling reuseaddr flag failed: " 
				<< string(strerror(errno)) << endl;
			return;
		}
	}

	int flags = fcntl(fd, F_GETFL, 0);
	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
		::close(fd);
		ERROR_STREAM << "Enabling O_NONBLOCK failed: " 
			<< string(strerror(errno)) << endl;
		return;
	}

	connect(fd, (sockaddr*)&sa, sizeof(sockaddr));
}

int Socket2::input_queue_length()
{
	int len;
	if (ioctl(fd, TIOCINQ, &len) == -1)
		return -1;
	return len;
}

int Socket2::output_queue_length()
{
	int len;
	if (ioctl(fd, TIOCOUTQ, &len) == -1)
		return -1;
	return len;
}

void Socket2::close()
{
	DEBUG_STREAM << "Closing the file descriptor..." << endl;

	int output_len = max(output_queue_length(), 0);
	int input_len = max(input_queue_length(), 0) + data.size();

	if(input_len + output_len)
	{
		WARN_STREAM << " Bytes dropped: " << input_len << " (" << data.size() << ") input, "
			<< output_len << " output" << endl;
	}

	if (::close(fd) == -1)
	{
		ERROR_STREAM << "Error closing file descriptor: "
			<< strerror(errno) << endl;
	}
	data.clear();
}

ssize_t Socket2::_write(int fd, const void *buf, size_t count)
{
	errno = 0;
	int ret = proto == UDP? 
		::sendto(fd, buf, count, 0, (sockaddr*) &sa, sa_len) :
		::write(fd, buf, count);
	conn_state = errno;
	return ret;
}

void Socket2::_read(size_t bytes_to_read)
{
	unsigned char buffer[10000];
	size_t bytes_total = data.size();
	ssize_t bytes_readed;

	while (bytes_total < bytes_to_read) {
		int s = select(READ);
		if (s == 0)
			goto timeout;
		else if (s < 0)
			goto error;
		else { /* s > 0 */ }

		size_t count = min((size_t)max(input_queue_length(), 0), sizeof(buffer));
		bytes_readed = proto == UDP?
			::recvfrom(fd, buffer, count, 0, (sockaddr*) &sa, &sa_len):
			::read(fd, buffer, count);

		if (bytes_readed > 0) {
			data.insert(data.end(), &buffer[0], &buffer[bytes_readed]);
			bytes_total += bytes_readed;
		} else if (bytes_readed == 0) {
			if (multiplexing == SELECT)
				goto error;
			/* Continue if multiplexing == SLEEP */
		}	else { /* bytes_readed < 0 */
			goto error;
		}
	}
	return;
error:
	check_state(true);
	sleep(tout);
timeout:
	Tango::Except::throw_exception(
			"", "Timeout expired", __PRETTY_FUNCTION__);
}

void Socket2::check_state(bool forcing)
{
	string mesg;

	if (forcing)
		(void)_write(fd, NULL, 0);

	switch(conn_state)
	{
		case 0: /* Success */
			set_state(Tango::ON);
			set_status("Connected");
		case EINTR:
		case EAGAIN:
			return;
		case EHOSTUNREACH:
			mesg = "No route to host";
			break;
		case ECONNREFUSED:
			mesg = "Connection refused";
			break;
		case EPIPE:
			mesg = "Broken pipe";
			break;
		case ETIMEDOUT:
			mesg = "Connection timed out";
			break;
		case ECONNRESET:
			mesg = "Connection reset by peer";
			break;
		case EISCONN:
		case EALREADY:
		case EINPROGRESS:
		case EADDRNOTAVAIL:
		case EAFNOSUPPORT:
		case EBADF:
		case ENOTSOCK:
		case EPROTOTYPE:
		case EIO:
		case ELOOP:
		case ENAMETOOLONG:
		case ENOENT:
		case ENOTDIR:
		default:
			ERROR_STREAM << "Socket error " << conn_state
				<< " not handled!" << endl;
			assert(false);
			break;
	}

	set_state(Tango::INIT);
	set_status("Reconnecting due: " + mesg);
	DEBUG_STREAM << "Reconnecting due: " << mesg << endl;

	close();
	resolve();
	open();
	reconnections += 1;
}

int Socket2::select(event_type et)
{
	if (multiplexing == SLEEP) {
		timeval twait;
		timerclear(&twait);
		twait.tv_usec = 10000;
		return sleep(twait);
	}

	// multiplexing == SELECT
	fd_set readfds;
	fd_set writefds;
	fd_set errorfds;

	FD_ZERO(&errorfds);
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);

	FD_SET(fd, &errorfds);
	switch (et) {
		case WRITE:
			FD_SET(fd, &writefds);
			break;
		case READ:
			FD_SET(fd, &readfds);
			break;
	}

	int select_ret = ::select(fd + 1,	&readfds,
			&writefds, &errorfds, &tout);

	if (FD_ISSET(fd, &errorfds)) {
		assert(false);
		return -1;
	}

	if (select_ret > 0) {
		if (et == READ && FD_ISSET(fd, &readfds))
			return select_ret;
		if (et == WRITE && FD_ISSET(fd, &writefds))
			return select_ret;
		assert(false);
		return -1;
	} else if (select_ret == 0) {
		return select_ret;
	} else { // select_ret < 0
		return select_ret;
	}
	assert(false);
	return -1;
}

void Socket2::resolve()
{
	DEBUG_STREAM << "Resolving " << hostname << "... " << endl;
	char ipstr[INET6_ADDRSTRLEN];

	sa_len = sizeof(sa);
	memset(&sa, 0, sa_len);

	addrinfo hints;
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	addrinfo *res, *p;

	if (inet_pton(AF_INET, hostname.c_str(), &(sa.sin_addr)) > 0) {
		sa.sin_family = AF_INET;
		sa.sin_port = htons(port);
	} else if (getaddrinfo(hostname.c_str(), NULL, &hints, &res) == 0) {
		for (p=res; p!=NULL; p=p->ai_next) {
			if (p->ai_family == AF_INET) {
				inet_ntop(p->ai_family,
						&(((sockaddr_in *)p->ai_addr)->sin_addr),
						ipstr, sizeof ipstr);
				sa.sin_addr = ((sockaddr_in *)p->ai_addr)->sin_addr;
				sa.sin_family = ((sockaddr_in *)p->ai_addr)->sin_family;
				sa.sin_port = htons(port);
			}
		}
		freeaddrinfo(res);
	}	else {
		ERROR_STREAM << "Name resolution failed" << endl;
	}
}

/*----- PROTECTED REGION END -----*/	//	Socket2::namespace_ending
} //	namespace