/*----- PROTECTED REGION ID(Serial2.cpp) ENABLED START -----*/
//=============================================================================
//
// file :        Serial2.cpp
//
// description : C++ source for the Serial2 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
//               Serial2 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.25 $
// $Date: 2019-01-10 15:58:39 $
//
// $HeadURL:  $
//
//=============================================================================
//                This file is generated by POGO
//        (Program Obviously used to Generate tango Object)
//=============================================================================


#include <Serial2.h>
#include <Serial2Class.h>

#include <fcntl.h>
#include <sys/ioctl.h>
#include <csignal>
#include <termios.h>

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

/**
 *  Serial2 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 Serial2_ns
{
/*----- PROTECTED REGION ID(Serial2::namespace_starting) ENABLED START -----*/

//	static initializations

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

//--------------------------------------------------------
/**
 *	Method     : Serial2::Serial2()
 *	Description: Constructors for a Tango device
 *                implementing the classSerial2
 */
//--------------------------------------------------------
Serial2::Serial2(Tango::DeviceClass *cl, std::string &s)
 : TANGO_BASE_CLASS(cl, s.c_str())
{
	/*----- PROTECTED REGION ID(Serial2::constructor_1) ENABLED START -----*/
	reconnections = -1;
	connecting = false;
	init_device();

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

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

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

	/*----- PROTECTED REGION END -----*/	//	Serial2::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(Serial2::init_device) ENABLED START -----*/
	
	//	Initialize device
	try
	{
		set_state( Tango::INIT );
		set_status( "Connecting..." );

		if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		{
			ERROR_STREAM << "Fail to ignore SIGPIPE" << endl;
		}

		open();
	}
	catch (Tango::DevFailed &e)
	{
		init_error = "Initialization failed: " + string(e.errors[0].desc);
	}
	catch (...)
	{
		init_error = "Initialization failed: Unknown error";
	}

	if( init_error.empty() )
	{
		check_connection( );
	}

	/*----- PROTECTED REGION END -----*/	//	Serial2::init_device
}

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

	mandatoryNotDefined = false;

	//	Read device properties from database.
	Tango::DbData	dev_prop;
	dev_prop.push_back(Tango::DbDatum("Serialline"));
	dev_prop.push_back(Tango::DbDatum("Charlength"));
	dev_prop.push_back(Tango::DbDatum("Parity"));
	dev_prop.push_back(Tango::DbDatum("Baudrate"));
	dev_prop.push_back(Tango::DbDatum("Stopbits"));
	dev_prop.push_back(Tango::DbDatum("Flowcontrol"));
	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 Serial2Class to get class property
		Tango::DbDatum	def_prop, cl_prop;
		Serial2Class	*ds_class =
			(static_cast<Serial2Class *>(get_device_class()));
		int	i = -1;

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

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

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

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

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

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

		//	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(Serial2::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;
		DEBUG_STREAM << "Using sleep IO multiplexing type" << endl;
	} else {
		multiplexing = SELECT;
		DEBUG_STREAM << "Using select IO multiplexing type" << endl;
	}

	/*----- PROTECTED REGION END -----*/	//	Serial2::get_device_property_after
}
//--------------------------------------------------------
/**
 *	Method     : Serial2::check_mandatory_property()
 *	Description: For mandatory properties check if defined in database.
 */
//--------------------------------------------------------
void Serial2::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(Serial2::check_mandatory_property) ENABLED START -----*/
		cerr << tms.str() << " for " << device_name << endl;
		
		/*----- PROTECTED REGION END -----*/	//	Serial2::check_mandatory_property
	}
}


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

	if (! init_error.empty())
	{
		set_state(Tango::FAULT);
		set_status(init_error);
		return;
	}

	tout.tv_sec = timeout / 1000;
	tout.tv_usec = timeout % 1000 * 1000;
	if ( ! timerisset( &tout ) )
	{
		set_state(Tango::FAULT);
		set_status("Invalid timeout");
	} else {
		check_connection( );
	}

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

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

//--------------------------------------------------------
/**
 *	Read attribute InputLength related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Serial2::read_InputLength(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Serial2::read_InputLength(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Serial2::read_InputLength) ENABLED START -----*/
	//	Set the attribute value
	attr_InputLength_read[0] = input_queue_length() + data.size(); 
	attr.set_value(attr_InputLength_read);
	
	/*----- PROTECTED REGION END -----*/	//	Serial2::read_InputLength
}
//--------------------------------------------------------
/**
 *	Read attribute OutputLength related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Serial2::read_OutputLength(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Serial2::read_OutputLength(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Serial2::read_OutputLength) ENABLED START -----*/
	//	Set the attribute value
	attr_OutputLength_read[0] = output_queue_length();
	attr.set_value(attr_OutputLength_read);
	
	/*----- PROTECTED REGION END -----*/	//	Serial2::read_OutputLength
}
//--------------------------------------------------------
/**
 *	Read attribute Reconnections related method
 *
 *
 *	Data type:	Tango::DevLong
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Serial2::read_Reconnections(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Serial2::read_Reconnections(Tango::Attribute &attr) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Serial2::read_Reconnections) ENABLED START -----*/
	//	Set the attribute value
	attr_Reconnections_read[0] = reconnections;
	attr.set_value(attr_Reconnections_read);
	
	/*----- PROTECTED REGION END -----*/	//	Serial2::read_Reconnections
}

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

//--------------------------------------------------------
/**
 *	Command Write related method
 *
 *
 *	@param argin
 */
//--------------------------------------------------------
void Serial2::write(const Tango::DevVarCharArray *argin)
{
	DEBUG_STREAM << "Serial2::Write()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Serial2::write) ENABLED START -----*/
	check_init();

	char *argin_data = new char[ argin->length() ];
	for( unsigned int i=0; i<argin->length(); ++i )
	{
		argin_data[i] = (*argin)[i];
	}

	int bytes_total = 0, bytes_to_write = argin->length();
	while (bytes_total != bytes_to_write &&  wait_for( WRITE, &tout ) )
	{
		int bytes_written;
		bytes_written = ::write(fd, argin_data + bytes_total, bytes_to_write - bytes_total);
		if (bytes_written < 0)
		{
			DEBUG_STREAM << strerror( errno ) << " (" << errno << ")" << endl;
			if( errno == EINTR )
			{
				continue;
			}

			if( errno == ECONNREFUSED )
			{
				delete argin_data;

				close();
				open();
				
				string error_mesg = "Connection refused";
				DEBUG_STREAM << error_mesg << endl;

				set_state( Tango::FAULT );
				set_status( error_mesg );

				sleep( tout.tv_sec );
				usleep( tout.tv_usec );
				timerclear( &tout );

				Tango::Except::throw_exception( "",
						error_mesg,
						"Serial2::write()");
			}

			ERROR_STREAM << "write() error not handled:" << endl;
			assert( false );
		}
		else if( bytes_written == 0 )
		{
			assert( false );
		}
		else /* bytes_written > 0 */
		{
			bytes_total += bytes_written;
		}
	}
	delete argin_data;
	
	timeval time_to_wait;
	time_to_wait.tv_sec = 0;
	time_to_wait.tv_usec = 1000;

	while ( output_queue_length() )
	{
		timeval newtimeout;
		timersub( &tout, &time_to_wait, &newtimeout );
		if( newtimeout.tv_sec >= 0 && newtimeout.tv_usec >= 0 )
		{
			sleep( time_to_wait.tv_sec );
			usleep( time_to_wait.tv_usec );

			tout = newtimeout;
			timeradd( &time_to_wait, &time_to_wait, &time_to_wait );
		}
		else
		{
			sleep( tout.tv_sec );
			usleep( tout.tv_usec );
			timerclear( &tout );
			break;
		}
	}

	if( (bytes_total - output_queue_length()) != bytes_to_write )
	{
		close();
		open();

		string error_mesg = "Unable to send request to device";
		DEBUG_STREAM << error_mesg << endl;

		set_state( Tango::FAULT );
		set_status( error_mesg );

		Tango::Except::throw_exception( "",
				error_mesg,
				"Serial2::write()");
	}

	/*----- PROTECTED REGION END -----*/	//	Serial2::write
}
//--------------------------------------------------------
/**
 *	Command Read related method
 *
 *
 *	@param argin
 *	@returns
 */
//--------------------------------------------------------
Tango::DevVarCharArray *Serial2::read(Tango::DevLong argin)
{
	Tango::DevVarCharArray *argout;
	DEBUG_STREAM << "Serial2::Read()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Serial2::read) ENABLED START -----*/
	check_init();

	if (argin < 0)
	{
		Tango::Except::throw_exception("",
				"Input has to be in positive range",
				"Serial2::read()");
	}

	while( (size_t)argin > data.size() )
	{
		if ( ! wait_for( READ, &tout ) )
		{
			string mesg( "No response from device" );
			DEBUG_STREAM << mesg << endl;
			Tango::Except::throw_exception("",
					mesg, "Serial2::read()");
		}
	}

	argout = new Tango::DevVarCharArray();
	argout->length(argin);
	for( int i=0; i<argin; ++i )
	{
		(*argout)[i] = data[i];
	}
	data.erase( data.begin(), data.begin() + argin );

	/*----- PROTECTED REGION END -----*/	//	Serial2::read
	return argout;
}
//--------------------------------------------------------
/**
 *	Command ReadUntil related method
 *
 *
 *	@param argin
 *	@returns
 */
//--------------------------------------------------------
Tango::DevVarCharArray *Serial2::read_until(const Tango::DevVarCharArray *argin)
{
	Tango::DevVarCharArray *argout;
	DEBUG_STREAM << "Serial2::ReadUntil()  - " << device_name << std::endl;
	/*----- PROTECTED REGION ID(Serial2::read_until) ENABLED START -----*/
	check_init();

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

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

	while( ! found )
	{
		for( pos = 0; pos < data.size(); ++pos )
		{
			if (memcmp(&data[pos], &delim, 1) == 0)
			{
				found = true;
				break;
			}
		}
		if ( ! found && ! wait_for( READ, &tout ) )
		{
			string mesg( "No response from device" );
			DEBUG_STREAM << mesg << endl;
			Tango::Except::throw_exception("",
					mesg, "Serial2::read_until()");
		}
	}


	argout = new Tango::DevVarCharArray();
	argout->length( pos+1 );
	for( size_t i = 0; i < pos + 1; ++i )
	{
		(*argout)[i] = data[i];
	}
	data.erase( data.begin(), data.begin() + pos + 1 );

	/*----- PROTECTED REGION END -----*/	//	Serial2::read_until
	return argout;
}
//--------------------------------------------------------
/**
 *	Method     : Serial2::add_dynamic_commands()
 *	Description: Create the dynamic commands if any
 *                for specified device.
 */
//--------------------------------------------------------
void Serial2::add_dynamic_commands()
{
	/*----- PROTECTED REGION ID(Serial2::add_dynamic_commands) ENABLED START -----*/
	
	//	Add your own code to create and add dynamic commands if any
	
	/*----- PROTECTED REGION END -----*/	//	Serial2::add_dynamic_commands
}

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

//	Additional Methods
void Serial2::check_init()
{
	if (! init_error.empty() )
	{
		DEBUG_STREAM << init_error << endl;
		Tango::Except::throw_exception( "",
				init_error.c_str(),
				"Serial2::check_init()");
	}
}

void Serial2::open()
{
	DEBUG_STREAM << "Creating the file descriptor..." << endl;

	if ((fd = ::open(serialline.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1)
	{
		string error_mesg = "Open device node failed: "
			+ string(strerror( errno ));
		ERROR_STREAM << error_mesg << endl;
		assert( false);
		Tango::Except::throw_exception( "",
				error_mesg,
				"Serial2::open()");
	}

	// Common
	struct termios options;
	memset(&options, 0, sizeof(struct termios));

	options.c_cflag |= (CLOCAL | CREAD);
	options.c_oflag &= ~OPOST;
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | IEXTEN);
	options.c_iflag &= ~(INPCK | ISTRIP);

	// Baud rate
	map<unsigned long, speed_t> baudrates_db;
	baudrates_db[4000000] = B4000000; baudrates_db[3500000] = B3500000;
	baudrates_db[3000000] = B3000000; baudrates_db[2500000] = B2500000;
	baudrates_db[2000000] = B2000000; baudrates_db[1500000] = B1500000;
	baudrates_db[1000000] = B1000000; baudrates_db[921600]  = B921600;
	baudrates_db[576000]  = B576000;  baudrates_db[500000]  = B500000;
	baudrates_db[460800]  = B460800;  baudrates_db[230400]  = B230400;
	baudrates_db[115200]  = B115200;  baudrates_db[57600]   = B57600;
	baudrates_db[38400]   = B38400;   baudrates_db[19200]   = B19200;
	baudrates_db[9600]    = B9600;    baudrates_db[4800]    = B4800;
	baudrates_db[2400]    = B2400;    baudrates_db[1800]    = B1800;
	baudrates_db[1200]    = B1200;    baudrates_db[600]     = B600;
	baudrates_db[300]     = B300;     baudrates_db[200]     = B200;
	baudrates_db[150]     = B150;     baudrates_db[134]     = B134;
	baudrates_db[110]     = B110;     baudrates_db[75]      = B75;
	baudrates_db[50]      = B50;
	cfsetispeed(&options, baudrates_db[baudrate]);
	cfsetospeed(&options, baudrates_db[baudrate]);

	// Data bits
	map<unsigned short, tcflag_t> databits_db;
	databits_db[8] = CS8;
	databits_db[7] = CS7;
	databits_db[6] = CS6;
	databits_db[5] = CS5;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= databits_db[charlength];

	// Stop bits
	switch (stopbits) {
		case 1:
			options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			{
				string error_mesg = "Error setting stop bits parameter!"
					+ string(strerror( errno ));
				ERROR_STREAM << error_mesg << endl;
				assert( false);
				Tango::Except::throw_exception( "",
						error_mesg,
						"Serial2::open()");
			}
	}

	// Parity bits
	transform(parity.begin(), parity.end(), parity.begin(), ::tolower);
	if (parity == "even") {
		options.c_cflag |= PARENB;
		options.c_cflag &= ~PARODD;
	} else if (parity == "odd") {
		options.c_cflag |= PARENB;
		options.c_cflag |= PARODD;
	} else if (parity == "none" ) {
		options.c_cflag &= ~PARENB;
	} else
	{
		string error_mesg = "Error setting parity bits parameter!"
			+ string(strerror( errno ));
		ERROR_STREAM << error_mesg << endl;
		assert( false);
		Tango::Except::throw_exception( "",
				error_mesg,
				"Serial2::open()");
	}

	// Flow control
	transform(flowcontrol.begin(), flowcontrol.end(), flowcontrol.begin(), ::tolower);
	options.c_iflag &= ~(IXON | IXOFF | IXANY);
	options.c_cflag &= ~CRTSCTS;


	if (flowcontrol == "xonxoff") {
		options.c_iflag |= (IXON | IXOFF | IXANY);
	} else if (flowcontrol == "rtscts") {
		options.c_cflag |= CRTSCTS;
	} else if (flowcontrol == "dtrdsr") {
		string error_mesg = "Flow control DTRDSR isn't supported yet!"
			+ string(strerror( errno ));
		ERROR_STREAM << error_mesg << endl;
		assert( false);
		Tango::Except::throw_exception( "",
				error_mesg,
				"Serial2::open()");
	} else if (flowcontrol == "none") {
		// Make nothing
	} else {
		string error_mesg = "Error setting flow control parameter!"
			+ string(strerror( errno ));
		ERROR_STREAM << error_mesg << endl;
		assert( false);
		Tango::Except::throw_exception( "",
				error_mesg,
				"Serial2::open()");
	}

	// Set necessary serial parameters
	if (tcsetattr(fd, TCSADRAIN, &options) == -1)
	{
		string error_mesg = "Error setting all serial parameters!"
			+ string(strerror( errno ));
		ERROR_STREAM << error_mesg << endl;
		assert( false);
		Tango::Except::throw_exception( "",
				error_mesg,
				"Serial2::open()");
	}

	connecting = true;
}

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

	connecting = false;

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

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

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

	data.clear();

	DEBUG_STREAM << "File descriptor closed" << endl;
}

int Serial2::input_queue_length()
{
	int len;
	if (ioctl(fd, FIONREAD, &len) == -1)
	{
		len = 0;
	}
	return len;
}

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

void Serial2::check_connection( )
{
	timeval tv;
	timerclear( &tv );
	if( wait_for( WRITE, &tv ) )
	{
		if( connecting )
		{
			reconnections++;
			connecting = false;
		}

		set_state( Tango::ON );
		set_status( "Connected" );
	}
}

bool Serial2::read(timeval *tv)
{
	int bytes_total = 0, bytes_to_read = input_queue_length();
	do
	{
		int len = (bytes_to_read - bytes_total) > BUFFER_SIZE?
			BUFFER_SIZE : (bytes_to_read - bytes_total);

		int bytes_readed = ::read(fd, buffer, len);

		if( bytes_readed < 0 )
		{
			DEBUG_STREAM << strerror( errno ) << " (" << errno << ")" << endl;
			if( errno == EINTR)
			{
				continue;
			}
		}
		else if( bytes_readed == 0 )
		{
			close();
			open();

			string error_mesg = "Server shutting down";
			DEBUG_STREAM << error_mesg << endl;

			set_state(Tango::FAULT);
			set_status(error_mesg);

			sleep( tv->tv_sec );
			usleep( tv->tv_usec );
			timerclear( tv );

			return false;
		}
		else /* bytes_readed > 0 */
		{
			data.insert(data.end(), &buffer[0], &buffer[bytes_readed]);
			bytes_total += bytes_readed;
		}
	}
	while (bytes_total != bytes_to_read);

	return true;
}

bool Serial2::wait_for( event_type et, timeval *tv )
{
	switch(multiplexing) {
		case SLEEP:
			return wait_for_with_sleep(et, tv);
		default:
			return wait_for_with_select(et, tv);
	}
}

bool Serial2::wait_for_with_sleep( event_type et, timeval *tv )
{
	struct timeval sleep_time;
	sleep_time.tv_sec = 0;
	sleep_time.tv_usec = 10000; /* 10 ms */

	do {
		switch( et )
		{
			case WRITE:
				if( output_queue_length() == 0 )
				{
#ifndef NDEBUG
					DEBUG_STREAM << "Ready to write" << endl;
#endif
					return true;
				}
				break;
			case READ:
				if( input_queue_length() != 0 )
				{
#ifndef NDEBUG
					DEBUG_STREAM << "Ready to read" << endl;
#endif
					return read(tv);
				}
				break;
		}

		int usleep_ret = usleep(sleep_time.tv_usec);
#ifndef NDEBUG
		DEBUG_STREAM << "usleep(): " << usleep_ret << endl;
#else
		(void)usleep_ret;
#endif
		timersub(tv, &sleep_time, tv);
	} while (timerisset(tv));

	return false;
}

bool Serial2::wait_for_with_select( event_type et, timeval *tv )
{
	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, tv );

#ifndef NDEBUG
	DEBUG_STREAM << "select(): " << select_ret << endl;
#endif

	if( select_ret == -1 )
	{
		ERROR_STREAM << "Select() error " << select_ret << " not handled:" 
			<< strerror( errno ) << endl;
		assert( false );
	}
	else if( select_ret == 0 )
	{
		return false;
	}

	if (FD_ISSET(fd, &errorfds))
	{
		ERROR_STREAM << "Select() error event not handled!" << endl;
		assert( false );
	}

	if (FD_ISSET(fd, &writefds))
	{
#ifndef NDEBUG
		DEBUG_STREAM << "Ready to write" << endl;
#endif
	}

	if (FD_ISSET(fd, &readfds))
	{
#ifndef NDEBUG
		DEBUG_STREAM << "Ready to read" << endl;
#endif
		return read(tv);
	}
	
	return true;
}

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