/*----- PROTECTED REGION ID(Starter.cpp) ENABLED START -----*/
//=============================================================================
//
// file :        Starter.cpp
//
// description : C++ source for the Starter 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
//               Starter are implemented in this file.
//
// project :     Starter for Tango Administration.
//
// $Author$
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015
//						European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// 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/>.
//
// $Revision$
// $Date$
//
//=============================================================================
//                This file is generated by POGO
//        (Program Obviously used to Generate tango Object)
//=============================================================================


#include <tango.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <StarterUtil.h>
#include <Starter.h>
#include <StarterClass.h>

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

/**
 *  Starter class description:
 *    This device server is able to control <b>Tango</b> components (database, device servers, clients...).
 *    It is able to start or stop and to report the status of these components.
 */

//================================================================
//  The following table gives the correspondence
//  between command and method names.
//
//  Command name          |  Method name
//================================================================
//  State                 |  dev_state
//  Status                |  Inherited (no method)
//  DevStart              |  dev_start
//  DevStop               |  dev_stop
//  DevStartAll           |  dev_start_all
//  DevStopAll            |  dev_stop_all
//  DevGetRunningServers  |  dev_get_running_servers
//  DevGetStopServers     |  dev_get_stop_servers
//  DevReadLog            |  dev_read_log
//  HardKillServer        |  hard_kill_server
//  NotifyDaemonState     |  notify_daemon_state
//  ResetStatistics       |  reset_statistics
//  UpdateServersInfo     |  update_servers_info
//================================================================

//================================================================
//  Attributes managed are:
//================================================================
//  NotifdState     |  Tango::DevState	Scalar
//  HostState       |  Tango::DevShort	Scalar
//  RunningServers  |  Tango::DevString	Spectrum  ( max = 1024)
//  StoppedServers  |  Tango::DevString	Spectrum  ( max = 1024)
//  Servers         |  Tango::DevString	Spectrum  ( max = 1024)
//================================================================

namespace Starter_ns
{
/*----- PROTECTED REGION ID(Starter::namespace_starting) ENABLED START -----*/

	//	static initializations

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

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

	init_device();

	/*----- PROTECTED REGION END -----*/	//	Starter::constructor_1
}
//--------------------------------------------------------
Starter::Starter(Tango::DeviceClass *cl, const char *s)
 : TANGO_BASE_CLASS(cl, s)
{
	/*----- PROTECTED REGION ID(Starter::constructor_2) ENABLED START -----*/

	init_device();

	/*----- PROTECTED REGION END -----*/	//	Starter::constructor_2
}
//--------------------------------------------------------
Starter::Starter(Tango::DeviceClass *cl, const char *s, const char *d)
 : TANGO_BASE_CLASS(cl, s, d)
{
	/*----- PROTECTED REGION ID(Starter::constructor_3) ENABLED START -----*/

	init_device();

	/*----- PROTECTED REGION END -----*/	//	Starter::constructor_3
}

//--------------------------------------------------------
/**
 *	Method      : Starter::delete_device()
 *	Description : will be called at device destruction or at init command
 */
//--------------------------------------------------------
void Starter::delete_device()
{
	DEBUG_STREAM << "Starter::delete_device() " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::delete_device) ENABLED START -----*/


	//	Check if shutting down (or Init command)
	if (Tango::Util::instance()->is_svr_shutting_down() ||
		Tango::Util::instance()->is_device_restarting(get_name()))
	{
		util->log_starter_info("Starter shutdown");

		//	Stop ping threads
		vector<ControlledServer>::iterator it;
		for (it=servers.begin() ; it<servers.end() ; ++it)
			it->thread_data->set_stop_thread();
		util->proc_util->stop_it();

		for (it=servers.begin() ; it<servers.end() ; ++it)
			it->thread->join(NULL);
		util->proc_util->join(NULL);

		//	Delete device allocated objects
		delete dbase;
		delete util;
		delete[] attr_HostState_read;
		delete[] attr_NotifdState_read;
		delete start_proc_data;
	}

	/*----- PROTECTED REGION END -----*/	//	Starter::delete_device
}

//--------------------------------------------------------
/**
 *	Method      : Starter::init_device()
 *	Description : will be called at device initialization.
 */
//--------------------------------------------------------
void Starter::init_device()
{
	DEBUG_STREAM << "Starter::init_device() create device " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::init_device_before) ENABLED START -----*/

	//	Initialization before get_device_property() call
	cout << "Starter::Starter() init device " << device_name << endl;

	/*----- PROTECTED REGION END -----*/	//	Starter::init_device_before
	

	//	Get the device properties from database
	get_device_property();
	
	/*----- PROTECTED REGION ID(Starter::init_device) ENABLED START -----*/

	debug = false;
	char	*dbg = getenv("DEBUG");
	if (dbg!=NULL)
		if (strcmp(dbg, "true")==0)
		{
			debug = true;
			cout << "!!! Debug mode is set !!!" << endl;
		}
	if (serverStartupTimeout<SERVER_TIMEOUT)
		serverStartupTimeout = SERVER_TIMEOUT;

	//	First time, check if instance and host name are coherent
	if (!debug)
		check_host();

	//	Do it only at startup and not at Init command
	//----------------------------------------------------
	if (Tango::Util::instance()->is_svr_starting() ||
		Tango::Util::instance()->is_device_restarting(get_name()))
	{
		//	Get database server name
		//--------------------------------------
		Tango::Util *tg = Tango::Util::instance();
		char	*dbname = tg->get_database()->get_dbase()->name();
		//	And connect database as DeviceProxy
		//--------------------------------------
		dbase = new Tango::DeviceProxy(dbname);
		CORBA::string_free(dbname);

		//	Build a shared data for StartProcessShared
		start_proc_data = new StartProcessShared();

		//	Get hostname (In case of cluster host could be multiple)
		//-------------------------------------------------------------
		vector<string>	hosts_list;
		char	*env = getenv("TANGO_CLUSTER");
        string host_name(tg->get_host_name().c_str());
		if (env==NULL)
			hosts_list.push_back(host_name);
		else
		if (strlen(env)==0)
			hosts_list.push_back(host_name);
		else
		{
			//	If MULTI_HOST is defined, parse host names
			//--------------------------------------------------
			string	str_list(env);
			cout << "hosts_list = " << str_list << endl;
			unsigned int	start = 0;
            unsigned	end = 0;
			while ((end= (int) str_list.find_first_of(":", (unsigned long) start)) > 0)
			{
				string	s = str_list.substr(start, end-start);
				hosts_list.push_back(s);
				start = end+1;
			}
			string	s = str_list.substr(start, str_list.length()-start);
			hosts_list.push_back(s);
			for (unsigned int i=0 ; i<hosts_list.size() ; i++)
				cout << hosts_list[i] << endl;
		}
		//	Create a StarterUtil instance
		//--------------------------------------
		util = new StarterUtil(dbase, hosts_list, logFileHome);
		util->log_starter_info("Starter startup");

		//	Initialize Attribute data member
		attr_HostState_read   = new Tango::DevShort[1];
		attr_NotifdState_read = new Tango::DevState[1];
		attr_NotifdState_read[0] = notifyd_state = Tango::UNKNOWN;

		//	Do not want exception during startup
		throwable = false;

		//	Wait a bit if necessary
		if (waitForDriverStartup>0)
		{
			cout << "Waiting " << waitForDriverStartup <<
					" seconds before starting (wait for drivers)." << endl;
			ms_sleep(1000*waitForDriverStartup);
		}

		//	Start notify daemon if not desabled and not already running
		if (useEvents)
		{
			try
			{
				cout << "Checking " << util->notifyd_name << endl;
				if (util->is_notifyd_alive()!=Tango::ON)
				{
					string	name(NOTIFY_DAEMON_SCRIPT);
					name += "/";
					name += tg->get_host_name();
					cout << "Starting " << name << endl;
					dev_start((char*)name.c_str());
				}
			}
			catch (...) {}
		}

		//	query database for controlled objects
		//	Wait for Database device is  OK
		bool	done = false;
		while (!done)
		{
			try {
				util->build_server_ctrl_object(&servers);
				do_update_from_db = false;
				done = true;
			}
			catch(Tango::DevFailed &e) {
				Tango::Except::print_exception(e);
			}
#			ifdef _TG_WINDOWS_
				_sleep(1000);
#			else
				sleep(1);
#			endif
		}

//	A a wait for first ping timeout !!!!
#	ifdef _TG_WINDOWS_
		_sleep(3000);
#	else
		sleep(3);
#	endif

		//	And Start servers for all startup levels.
		//	The interStartupLevelWait value will be managed
		//		by the start process thread.
		//---------------------------------------------------
		int nb_levels =
			((static_cast<StarterClass *>(get_device_class()))->nbStartupLevels);

		if (startServersAtStartup==true)
		{
			//	Update state before
			for (unsigned int i=0 ; i<servers.size() ; i++)
			{
				ControlledServer	*server = &servers[i];
				server->set_state(server->thread_data->get_state());
				server->nbInstances = server->thread_data->getNbInstaces();
			}
			//	And then starl-c16-1 (ZMQ)t levels
			for (int level=1 ; level<=nb_levels ; level++)
			{
				throwable = false;
				try {
					dev_start_all((Tango::DevShort)level);
				}
				catch (Tango::DevFailed &e) {
					cerr << e.errors[0].desc << endl;
				}
				ms_sleep(50);
			}
		}

		//	Want exception during normal run
		throwable = true;

		//	Set the default state
		set_state(Tango::MOVING);
		*attr_HostState_read = get_state();

		check_log_dir();

		//	Update Loggs
		WARN_STREAM << "Starter Server Started !" << endl;
	}

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

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

	//	Initialize property data members
	fireFromDbase = true;

	/*----- PROTECTED REGION END -----*/	//	Starter::get_device_property_before


	//	Read device properties from database.
	Tango::DbData	dev_prop;
	dev_prop.push_back(Tango::DbDatum("AutoRestartDuration"));
	dev_prop.push_back(Tango::DbDatum("InterStartupLevelWait"));
	dev_prop.push_back(Tango::DbDatum("KeepLogFiles"));
	dev_prop.push_back(Tango::DbDatum("LogFileHome"));
	dev_prop.push_back(Tango::DbDatum("ServerStartupTimeout"));
	dev_prop.push_back(Tango::DbDatum("StartDsPath"));
	dev_prop.push_back(Tango::DbDatum("StartServersAtStartup"));
	dev_prop.push_back(Tango::DbDatum("UseEvents"));
	dev_prop.push_back(Tango::DbDatum("WaitForDriverStartup"));
	dev_prop.push_back(Tango::DbDatum("MovingMaxDuration"));

	//	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 StarterClass to get class property
		Tango::DbDatum	def_prop, cl_prop;
		StarterClass	*ds_class =
			(static_cast<StarterClass *>(get_device_class()));
		int	i = -1;

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

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

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

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

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

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

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

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

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

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

	}

	/*----- PROTECTED REGION ID(Starter::get_device_property_after) ENABLED START -----*/

	//	Check device property data members init
	if (startDsPath.empty())
		startDsPath.push_back(".");
	else
	for (unsigned int i=0 ; i<startDsPath.size() ; i++)
		INFO_STREAM << "startDsPath[" << i << "] = " << startDsPath[i] << endl;
	INFO_STREAM << "WaitForDriverStartup = " << waitForDriverStartup << " seconds" << endl;
	cout << "UseEvents  = " << ((useEvents==false)? "False": "True") << endl;
	cout << "interStartupLevelWait  = " << interStartupLevelWait << endl;
	cout << "serverStartupTimeout   = " << serverStartupTimeout << endl;



	//	Get the fireFromDbase value from Default object
	Tango::DbData	data;
	data.push_back(Tango::DbDatum("FireToStarter"));
	Tango::Util *tg = Tango::Util::instance();
	tg->get_database()->get_property("Default", data);
	string	tmp;
	if (data[0].is_empty()==false)
		data[0]  >>  tmp;
	transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
	if (tmp=="false")
		fireFromDbase = false;
	cout << "fireFromDbase  = " << fireFromDbase << endl;
	cout << "logFileHome    = " << logFileHome   << endl;
	cout << "StartServersAtStartup = " << startServersAtStartup  << endl;
	cout << "AutoRestartDuration   = " << autoRestartDuration  << endl;

	/*----- PROTECTED REGION END -----*/	//	Starter::get_device_property_after
}

//--------------------------------------------------------
/**
 *	Method      : Starter::always_executed_hook()
 *	Description : method always executed before any command is executed
 */
//--------------------------------------------------------
void Starter::always_executed_hook()
{
	DEBUG_STREAM << "Starter::always_executed_hook()  " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::always_executed_hook) ENABLED START -----*/


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

//--------------------------------------------------------
/**
 *	Method      : Starter::read_attr_hardware()
 *	Description : Hardware acquisition for attributes
 */
//--------------------------------------------------------
void Starter::read_attr_hardware(TANGO_UNUSED(vector<long> &attr_list))
{
	DEBUG_STREAM << "Starter::read_attr_hardware(vector<long> &attr_list) entering... " << endl;
	/*----- PROTECTED REGION ID(Starter::read_attr_hardware) ENABLED START -----*/

	//	Update if Servers attribute (polled) is called.
	for (unsigned int i=0 ; i < attr_list.size() ; i++)
	{
		Tango::WAttribute &att = dev_attr->get_w_attr_by_ind(attr_list[i]);
		string attr_name(att.get_name().c_str());
		if (attr_name == "Servers")
			for (unsigned int j=0 ; j<servers.size() ; j++)
			{
				Tango::DevState	previous_state = servers[j].get_state();
				//	Update server state
				servers[j].set_state(servers[j].thread_data->get_state());
				servers[j].nbInstances = servers[j].thread_data->getNbInstaces();

				//	Check if state has changed.
				if (previous_state!=servers[j].get_state())
					manage_changing_state(&servers[j], previous_state);

				//cout << "read_attr_hardware:[" << servers[j].name << "]	" <<
				//				Tango::DevStateName[servers[j].state]  << endl;
			}
	}

	/*----- PROTECTED REGION END -----*/	//	Starter::read_attr_hardware
}

//--------------------------------------------------------
/**
 *	Read attribute NotifdState related method
 *	Description: return ON or FAULT if notify daemon is running or not.
 *
 *	Data type:	Tango::DevState
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Starter::read_NotifdState(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Starter::read_NotifdState(Tango::Attribute &attr) entering... " << endl;
	/*----- PROTECTED REGION ID(Starter::read_NotifdState) ENABLED START -----*/

	//	Set the attribute value
	attr_NotifdState_read[0] = notifyd_state;
	attr.set_value(attr_NotifdState_read);

	/*----- PROTECTED REGION END -----*/	//	Starter::read_NotifdState
}
//--------------------------------------------------------
/**
 *	Read attribute HostState related method
 *	Description: 
 *
 *	Data type:	Tango::DevShort
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Starter::read_HostState(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Starter::read_HostState(Tango::Attribute &attr) entering... " << endl;
	/*----- PROTECTED REGION ID(Starter::read_HostState) ENABLED START -----*/

	//	Set the attribute value
	*attr_HostState_read = (short) get_state();
	DEBUG_STREAM << "HostState = " << attr_HostState_read[0] << endl;
	attr.set_value(attr_HostState_read);

	/*----- PROTECTED REGION END -----*/	//	Starter::read_HostState
}
//--------------------------------------------------------
/**
 *	Read attribute RunningServers related method
 *	Description: 
 *
 *	Data type:	Tango::DevString
 *	Attr type:	Spectrum max = 1024
 */
//--------------------------------------------------------
void Starter::read_RunningServers(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Starter::read_RunningServers(Tango::Attribute &attr) entering... " << endl;
	/*----- PROTECTED REGION ID(Starter::read_RunningServers) ENABLED START -----*/

	//	Check running ones
	vector<string>	runnings;
	for (unsigned int i=0 ; i<servers.size() ; i++)
		if (servers[i].get_state()==Tango::ON)
			runnings.push_back(servers[i].name);
	if (runnings.empty()) {
		attr.set_value(dummyStringArray, 0);
	}
	else {
		//	And fill attribute
		stringArrayRunning << runnings;
		attr.set_value(stringArrayRunning.get_buffer(), stringArrayRunning.length());
	}
	/*----- PROTECTED REGION END -----*/	//	Starter::read_RunningServers
}
//--------------------------------------------------------
/**
 *	Read attribute StoppedServers related method
 *	Description: Return all the Stopped servers.
 *
 *	Data type:	Tango::DevString
 *	Attr type:	Spectrum max = 1024
 */
//--------------------------------------------------------
void Starter::read_StoppedServers(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Starter::read_StoppedServers(Tango::Attribute &attr) entering... " << endl;
	/*----- PROTECTED REGION ID(Starter::read_StoppedServers) ENABLED START -----*/

	//	Check stopped ones
	vector<string>	stopped;
	for (unsigned int i=0 ; i<servers.size() ; i++)
		if (servers[i].get_state()!=Tango::ON)
			stopped.push_back(servers[i].name);
	if (stopped.empty()) {
		attr.set_value(dummyStringArray, 0);
	}
	else {
		//	And fill attribute
		stringArrayStopped << stopped;
		attr.set_value(stringArrayStopped.get_buffer(), stringArrayStopped.length());
	}
	/*----- PROTECTED REGION END -----*/	//	Starter::read_StoppedServers
}
//--------------------------------------------------------
/**
 *	Read attribute Servers related method
 *	Description: Return all registered servers for this host.
 *               Server names are followed by:   [states] [controlled] [level] [nb instances]
 *               If nb instances >1 a warning will be displayed in Astor
 *
 *	Data type:	Tango::DevString
 *	Attr type:	Spectrum max = 1024
 */
//--------------------------------------------------------
void Starter::read_Servers(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Starter::read_Servers(Tango::Attribute &attr) entering... " << endl;
	/*----- PROTECTED REGION ID(Starter::read_Servers) ENABLED START -----*/

	//	Check starting ones
	vector<string>	vs;
	for (unsigned int i=0 ; i<servers.size() ; i++)
	{
		TangoSys_OMemStream tms;
		tms << servers[i].name << '\t'
            << Tango::DevStateName[servers[i].get_state()] << '\t'
            << servers[i].controlled  << '\t'
            << servers[i].startup_level << '\t' << servers[i].nbInstances;
		string	s = tms.str();
		vs.push_back(s);

	}
	if (vs.empty())
		attr.set_value(dummyStringArray, 0);
	else {
		//	And fill attribute
		stringArrayServers << vs;
		attr.set_value(stringArrayServers.get_buffer(), stringArrayServers.length());
	}
	/*----- PROTECTED REGION END -----*/	//	Starter::read_Servers
}

//--------------------------------------------------------
/**
 *	Method      : Starter::add_dynamic_attributes()
 *	Description : Create the dynamic attributes if any
 *                for specified device.
 */
//--------------------------------------------------------
void Starter::add_dynamic_attributes()
{
	/*----- PROTECTED REGION ID(Starter::add_dynamic_attributes) ENABLED START -----*/

	//	Add your own code to create and add dynamic attributes if any

	/*----- PROTECTED REGION END -----*/	//	Starter::add_dynamic_attributes
}

//--------------------------------------------------------
/**
 *	Command State related method
 *	Description: This command gets the device state (stored in its <i>device_state</i> data member) and returns it to the caller.
 *
 *	@returns State Code
 */
//--------------------------------------------------------
Tango::DevState Starter::dev_state()
{
	DEBUG_STREAM << "Starter::State()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_state) ENABLED START -----*/

	Tango::DevState	argout;
		//	Add your own state management
	//	Check if last command is more than readInfoDbPeriod class property
	int	period =
		((static_cast<StarterClass *>(get_device_class()))->readInfoDbPeriod);

	//	If not fired -> do it myself by polling
	//---------------------------------------------
	if (fireFromDbase==false)
	{
		static time_t	t0 = 0;
	    	   time_t	t1 = time(NULL);
		//	If less -> no update
		if (t1-t0 >= period)
		{
			t0 = t1;

			//	Update control obj from database (could have been modified)
			INFO_STREAM << "Updating from data base" << endl;
			util->build_server_ctrl_object(&servers);
		}
	}
	else
	if (do_update_from_db)
	{
		//	Has been fired from Dbase
		util->build_server_ctrl_object(&servers);
		do_update_from_db = false;
	}
	//	Check for notify daemon state if requested
	//---------------------------------------------
	if (useEvents)
		notifyd_state = util->is_notifyd_alive();
	else
		notifyd_state = Tango::ON;

	//	Check if servers object initilized
	//---------------------------------------
	if (servers.empty())
	{
		INFO_STREAM << "Exiting dev_state() with servers.size() null" << endl;
		if (notifyd_state==Tango::ON)
			argout = Tango::ON;
		else
			argout = Tango::ALARM;
	}
	else
	{
		//	Check how many servers are running
		ControlledServer *p_serv;
		int		nb_running = 0;
		int		nb_controlled = 0;
		int		nb_moving = 0;
		int		nb_long_time_moving = 0;
		int		nb_stopped = 0;
		int		nb_instances = 0;
		for (unsigned int i=0 ; i<servers.size() ; i++)
		{
			p_serv = &servers[i];
			//	Count how many are controlled, running, stopped,....
			if (p_serv->controlled)
			{
				nb_controlled++;
				//	Fix witch one is running and count how many controlled are running
				if ((p_serv->get_state()==Tango::ON)) {
				    if (p_serv->nbInstances>1)
				        nb_instances++;
                    else
    				    nb_running++;
				}
				else
				if (p_serv->get_state()==Tango::MOVING) {
                    //cout << p_serv->get_moving_duration() << endl;
                    if (p_serv->get_moving_duration()>movingMaxDuration)
                        nb_long_time_moving++;
                    else
                        nb_moving++;
                }
                else
                    nb_stopped++;
			}
		}

		//	compare nb running with nb_controlled to set state
		if (nb_moving>0 || start_proc_data->get_starting_processes()>0) {
            set_status("At least one of the  controlled servers is running but not responding");
            argout = Tango::MOVING;
        }
		else
        if (nb_long_time_moving>0) {
            set_status("At least one of the  controlled servers is running but not responding since a while");
            argout = Tango::STANDBY;
        }
        else
		if (nb_running==nb_controlled && notifyd_state==Tango::ON) {
		    if (nb_instances>0) {
                argout = Tango::ALARM;
                set_status("At least one server is running twice");
            } else {
                argout = Tango::ON;
                set_status("All controlled servers are running");
            }
        }
		else
		if (nb_stopped==nb_controlled) {
            set_status("All controlled servers are not running");
            argout = Tango::OFF;
        }
        else {
            argout = Tango::ALARM;
            set_status("At least one of the  controlled servers is not running");
        }
	}

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_state
	set_state(argout);    // Give the state to Tango.
	if (argout!=Tango::ALARM)
		Tango::DeviceImpl::dev_state();
	return get_state();  // Return it after Tango management.
}
//--------------------------------------------------------
/**
 *	Command DevStart related method
 *	Description: Start the specified server.
 *
 *	@param argin Server to be started.
 */
//--------------------------------------------------------
void Starter::dev_start(Tango::DevString argin)
{
	DEBUG_STREAM << "Starter::DevStart()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_start) ENABLED START -----*/
	try {
		NewProcess	*np = processCouldStart(argin);
		if (np==NULL)
			return;
		//	Build a vector to start process
		vector<NewProcess *>	processes;
		processes.push_back(np);
		startProcesses(processes, 0);

		//	Started with starter -> stopped switched to false.
		string servname(argin);
		ControlledServer *server = util->get_server_by_name(servname, servers);
		if (server!=NULL) {
			server->stopped = false;
			server->started_time = time(NULL);
		}
	}
	catch (Tango::DevFailed &) {
		throw;
	}
	catch (exception &e) {
		cerr << "================================" << endl;
		cerr << e.what() << endl;
		cerr <<	"================================" << endl;
		TangoSys_OMemStream tms;
		tms << "Starting process failed:   " << e.what();
		Tango::Except::throw_exception(
			   (const char *)"START_PROCASS_FAILDE",
			   tms.str().c_str(),
			   (const char *)"Starter::dev_start()");
	}
	catch (...) {
		cerr << "================================" << endl <<
				"    Unknown exception caught"    << endl <<
				"================================" << endl;
		Tango::Except::throw_exception(
			   (const char *)"START_PROCASS_FAILDE",
			   (const char *)"Starting process failed:    Unknown exception caught",
			   (const char *)"Starter::dev_start()");
	}

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_start
}
//--------------------------------------------------------
/**
 *	Command DevStop related method
 *	Description: Stop the specified server.
 *
 *	@param argin Servero be stopped.
 */
//--------------------------------------------------------
void Starter::dev_stop(Tango::DevString argin)
{
	DEBUG_STREAM << "Starter::DevStop()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_stop) ENABLED START -----*/

	//	Add your own code
	//	Check if servers object initilized
	//---------------------------------------
	if (servers.empty())
	{
		TangoSys_OMemStream out_stream;
		out_stream << argin << ": Server  not controlled !" << ends;
		Tango::Except::throw_exception(out_stream.str(),
				out_stream.str(),
				(const char *)"Starter::dev_stop()");
		return;
	}

	//	Check Argin as server name
	//----------------------------------
	string	name(argin);
	ControlledServer *server = util->get_server_by_name(name, servers);
	if (server==NULL)
	{
		TangoSys_OMemStream out_stream;
		out_stream << argin << ": Unknown Server !" << ends;
		Tango::Except::throw_exception(out_stream.str(),
				out_stream.str(),
				(const char *)"Starter::dev_stop()");
		return;
	}

	//	Make shure that it's  running.
	//---------------------------------------
	if (server->get_state()==Tango::ON)
	{
		//	And Kill it with kill signal
		Tango::DeviceProxy *dev = NULL;
        try {
		    dev = new Tango::DeviceProxy(server->admin_name);
            dev->command_inout("Kill");
            delete dev;
       }
        catch (Tango::DevFailed &e) {
            if (dev!=NULL)
                delete dev;
            throw e;
        }

		TangoSys_OMemStream out_stream;
		out_stream << argin << " stopped";
		WARN_STREAM << out_stream.str() << endl;
		cout << out_stream.str() << endl;
		util->log_starter_info(out_stream.str());
		server->stopped = true;
	}
	else
	if (server->get_state()==Tango::MOVING)
	{
		TangoSys_OMemStream out_stream;
		out_stream << argin << " is running but not responding !" << ends;
		Tango::Except::throw_exception(
				(const char *)"SERVER_NOT_RESPONDING",
				out_stream.str(),
				(const char *)"Starter::dev_stop()");
		return;
	}
	else
	{
		TangoSys_OMemStream out_stream;
		out_stream << argin << " is NOT running !" << ends;
		Tango::Except::throw_exception(
				(const char *)"SERVER_NOT_RUNNING",
				out_stream.str(),
				(const char *)"Starter::dev_stop()");
		return;
	}

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_stop
}
//--------------------------------------------------------
/**
 *	Command DevStartAll related method
 *	Description: Start all device servers controlled on the host for the argin level.
 *
 *	@param argin Startup level.
 */
//--------------------------------------------------------
void Starter::dev_start_all(Tango::DevShort argin)
{
	DEBUG_STREAM << "Starter::DevStartAll()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_start_all) ENABLED START -----*/

	Tango::DevShort  level = argin;

	//	Check if level is still active
	if (start_proc_data->level_is_still_active(level)) {
		TangoSys_OMemStream tms;
		tms << "Level " << level << " is already starting" << endl;
		Tango::Except::throw_exception(
				"LevelAlreadyStarting", tms.str().c_str(), "Starter::dev_start_all()");
	}
	//	Check if servers object initialized
	if (servers.empty()) {
		if (throwable) {
			TangoSys_OMemStream out_stream;
			out_stream << "NO Server  controlled !" << ends;
			Tango::Except::throw_exception(out_stream.str(),
			out_stream.str(),
				(const char *)"Starter::dev_start_all()");
		}
	}

	//	Do not want exception during start up
	throwable = false;

	//	And start the stopped ones
	vector<NewProcess *> processes;
	for (unsigned int i=0 ; i<servers.size() ; i++)
	{
		ControlledServer *server = &servers[i];
		//	server->running could not be initialized
		if (server->controlled  &&  server->startup_level==level)
		{
			cout << "Check startup for " << server->name << endl;
			if (server->get_state()==Tango::FAULT)
			{
				NewProcess	*np = processCouldStart((char*)server->name.c_str());
				if (np!=NULL)
				{
					processes.push_back(np);
					cout << "Try to start " << np->serverName << endl;
				}
				else
					cout << "np is null (?)" << endl;
			}
			else
				cout << "	Already running...."<< endl;
		}
	}
	if (processes.empty()==false)
		startProcesses(processes, level);

	//	Want exception during normal run
	throwable = true;

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_start_all
}
//--------------------------------------------------------
/**
 *	Command DevStopAll related method
 *	Description: Stop all device servers controlled on the host for the argin level.
 *
 *	@param argin Startup Level.
 */
//--------------------------------------------------------
void Starter::dev_stop_all(Tango::DevShort argin)
{
	DEBUG_STREAM << "Starter::DevStopAll()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_stop_all) ENABLED START -----*/

	//	Add your own code
	Tango::DevShort  level = argin;

	//	Check if servers object initialized
	if (servers.empty())
	{
		TangoSys_OMemStream out_stream;
		out_stream << "NO Server  controlled !" << ends;
		Tango::Except::throw_exception(out_stream.str(),
				out_stream.str(),
				(const char *)"Starter::dev_stop_all()");
		return;
	}

	//  Remove level from list to be started
	cout << "Starter removing level " << level << endl;
    start_proc_data->remove_level(level);

	//	And stop the running ones
	for (unsigned int i=0 ; i<servers.size() ; i++)
	{
		ControlledServer *server = &servers[i];
		if (server->controlled            &&
			server->startup_level==level  &&
			server->get_state()==Tango::ON)
				dev_stop((char*)server->name.c_str());
	}

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_stop_all
}
//--------------------------------------------------------
/**
 *	Command DevGetRunningServers related method
 *	Description: Control the running process from property list.
 *               And return the list of the processes which are really running.
 *
 *	@param argin True for all servers. False for controlled servers only.
 *	@returns List of the processes which are running.
 */
//--------------------------------------------------------
Tango::DevVarStringArray *Starter::dev_get_running_servers(Tango::DevBoolean argin)
{
	Tango::DevVarStringArray *argout;
	DEBUG_STREAM << "Starter::DevGetRunningServers()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_get_running_servers) ENABLED START -----*/

	//	Add your own code
	Tango::DevBoolean  all_serv = argin;
	argout = new Tango::DevVarStringArray;
	INFO_STREAM << "Starter::dev_get_running_server(): entering... !" << endl;

	//	Check if servers object initilized
	//---------------------------------------
	if (servers.empty())
	{
		return argout;
	}

	//	prepare the argout for running servers list
	//-----------------------------------------------------------
	int	nb = 0;
	int	x;
	unsigned int	i;
	for (i=0 ; i<servers.size() ; i++)
		if (all_serv || servers[i].controlled)
			if (servers[i].get_state()==Tango::ON)
				nb ++;

	//	And fill it
	//-----------------------------------------------------------
	argout->length((_CORBA_ULong)nb);
	for (i=0, x=0 ; i<servers.size() && x<nb ; i++)
		if (all_serv || servers[i].controlled)
			if (servers[i].get_state()==Tango::ON)
			{
				INFO_STREAM << "RUNNING: " << servers[i].name << endl;
				(*argout)[x++] = CORBA::string_dup(servers[i].name.c_str());
			}

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_get_running_servers
	return argout;
}
//--------------------------------------------------------
/**
 *	Command DevGetStopServers related method
 *	Description: Control the running process from property list.
 *               And return the list of the processes which are not running.
 *
 *	@param argin True for all servers. False for controlled servers only.
 *	@returns List of the processes which are not running.
 */
//--------------------------------------------------------
Tango::DevVarStringArray *Starter::dev_get_stop_servers(Tango::DevBoolean argin)
{
	Tango::DevVarStringArray *argout;
	DEBUG_STREAM << "Starter::DevGetStopServers()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_get_stop_servers) ENABLED START -----*/

	//	Add your own code
	Tango::DevBoolean  all_serv = argin;
	argout = new Tango::DevVarStringArray();
	INFO_STREAM << "Starter::dev_get_stop_servers(): entering... !" << endl;

	//	Check if servers object initilized
	//---------------------------------------
	if (servers.empty())
	{
		argout->length(0);
		return argout;
	}

	//	prepeare the argout for NOT running servers list
	//-----------------------------------------------------------
	int		nb = 0;
	int		x;
	unsigned int	i;
	for (i=0 ; i<servers.size() ; i++)
		if (all_serv || servers[i].controlled)
			if (servers[i].get_state()!=Tango::ON)
				nb ++;

	//	And fill it
	//-----------------------------------------------------------
	argout->length((_CORBA_ULong)nb);
	for (i=0, x=0  ; i<servers.size() && x<nb; i++)
		if (all_serv || servers[i].controlled)
			if (servers[i].get_state()!=Tango::ON)
			{
				INFO_STREAM << "STOPPED: " << servers[i].name << endl;
				(*argout)[x++] = CORBA::string_dup(servers[i].name.c_str());
			}

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_get_stop_servers
	return argout;
}
//--------------------------------------------------------
/**
 *	Command DevReadLog related method
 *	Description: At server startup, its standard error is redirected to a log file.
 *               This command will read this file and return the read string from the file.
 *
 *	@param argin server name and domain (e.g. Starter/corvus)
 *               If argin ==``Starter``     -> return Starter logg file content.
 *               If argin ==``Statistics``  -> return Starter statistics file content.
 *	@returns String found in log file.
 */
//--------------------------------------------------------
Tango::ConstDevString Starter::dev_read_log(Tango::DevString argin)
{
	Tango::ConstDevString argout;
	DEBUG_STREAM << "Starter::DevReadLog()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::dev_read_log) ENABLED START -----*/

	//	Add your own code
	string	filename;
	bool	on_starter;
	//	Check if for Starter itself
	if (strcmp(argin, "Starter")==0)
	{
		on_starter = true;
		filename = util->starter_log_file;
	}
	else
	if (strcmp(argin, "Statistics")==0)
	{
		on_starter = true;
		filename = util->starter_stat_file;
	}
	else
	{
		on_starter = false;
		filename = util->build_log_file_name(argin);
	}

	//	Try to open log file
	ifstream	ifs((char *)filename.c_str());
	if (!ifs)
	{
		//	Open log file failed -> Throw exception
		//----------------------------------------------
		TangoSys_OMemStream reason;
		TangoSys_OMemStream description;
		reason << "Cannot open " << filename << ends;
		description << strerror(errno);
		Tango::Except::throw_exception(reason.str(),
						description.str(),
						(const char *)"Starter::dev_read_log");
	}

	//	Read and close log file, and return string read from it.
	//-------------------------------------------------------------
	stringstream	strlog;
	if (!on_starter)
	{
		strlog << filename << endl;
		strlog << util->get_file_date((char *)filename.c_str()) << endl << endl;
	}
	strlog << ifs.rdbuf() << ends;
	ifs.close();
	returned_str = strlog.str();
	argout = returned_str.c_str();

	/*----- PROTECTED REGION END -----*/	//	Starter::dev_read_log
	return argout;
}
//--------------------------------------------------------
/**
 *	Command HardKillServer related method
 *	Description: Hard kill a server (kill -9)
 *
 *	@param argin Server name
 */
//--------------------------------------------------------
void Starter::hard_kill_server(Tango::DevString argin)
{
	DEBUG_STREAM << "Starter::HardKillServer()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::hard_kill_server) ENABLED START -----*/

	//	Add your own code
	string	servname(argin);
	int	pid = util->proc_util->get_server_pid(servname);
	if (pid<0)
	{
		TangoSys_OMemStream tms;
		tms << "Server " << argin << " is not running !";
		Tango::Except::throw_exception(
					(const char *)"SERVER_NOT_RUNNING",
					tms.str().c_str(),
					(const char *)"Starter::hard_kill_server()");
	}
#ifdef _TG_WINDOWS_

	HANDLE	handle = NULL;				//- process addr (in the heap)
	if( (handle=OpenProcess(PROCESS_TERMINATE, false, pid)) == NULL)
	{
		TangoSys_OMemStream tms;
		tms << "Open handle on server " << argin << " failed !";
		Tango::Except::throw_exception(
					(const char *)"KILL_DERVER_FAILED",
					tms.str().c_str(),
					(const char *)"Starter::hard_kill_server()");
	}

	TerminateProcess(handle, 0);
	CloseHandle(handle);
	if (GetLastError()!= ERROR_SUCCESS)
	{
		TangoSys_OMemStream tms;
		tms << "Kill server " << argin << " failed !";
		Tango::Except::throw_exception(
					(const char *)"KILL_DERVER_FAILED",
					tms.str().c_str(),
					(const char *)"Starter::hard_kill_server()");
	}

#else

	TangoSys_OMemStream cmd;
	cmd << "kill -9 " << pid;
	if (system(cmd.str().c_str())<0)
	{
		TangoSys_OMemStream tms;
		tms << "Kill server " << argin << " failed !";
		Tango::Except::throw_exception(
					(const char *)"KILL_DERVER_FAILED",
					tms.str().c_str(),
					(const char *)"Starter::hard_kill_server()");
	}
#endif

	/*----- PROTECTED REGION END -----*/	//	Starter::hard_kill_server
}
//--------------------------------------------------------
/**
 *	Command NotifyDaemonState related method
 *	Description: Returns the Notify Daemon state.
 *
 *	@returns Tango::ON if Notify daemon is running else Tango::FAULT.
 */
//--------------------------------------------------------
Tango::DevState Starter::notify_daemon_state()
{
	Tango::DevState argout;
	DEBUG_STREAM << "Starter::NotifyDaemonState()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::notify_daemon_state) ENABLED START -----*/

	//	Add your own code
	if (useEvents==false)
		Tango::Except::throw_exception(
					(const char *)"NOTIFY_NOT_AVAILABLE",
					(const char *)"Notify Daemon control is disabled",
					(const char *)"Starter::notify_daemon_state()");
	argout = notifyd_state;

	/*----- PROTECTED REGION END -----*/	//	Starter::notify_daemon_state
	return argout;
}
//--------------------------------------------------------
/**
 *	Command ResetStatistics related method
 *	Description: Reset statistics file.
 *
 */
//--------------------------------------------------------
void Starter::reset_statistics()
{
	DEBUG_STREAM << "Starter::ResetStatistics()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::reset_statistics) ENABLED START -----*/

	//	Add your own code
	util->reset_starter_stat_file(&servers);;

	/*----- PROTECTED REGION END -----*/	//	Starter::reset_statistics
}
//--------------------------------------------------------
/**
 *	Command UpdateServersInfo related method
 *	Description: Indicate to the device server than the information about servers to be controlled has been modified.
 *               The device server must read the database to update the servers info list.
 *               If the default case, this command is sent by Database server itself.
 *
 */
//--------------------------------------------------------
void Starter::update_servers_info()
{
	DEBUG_STREAM << "Starter::UpdateServersInfo()  - " << device_name << endl;
	/*----- PROTECTED REGION ID(Starter::update_servers_info) ENABLED START -----*/

	//	Add your own code
	do_update_from_db = true;

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

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

	//	Additional Methods

//+------------------------------------------------------------------
/**
 *	Check if a process could be started (file exists, is not running, ...)
 */
//+------------------------------------------------------------------
NewProcess *Starter::processCouldStart(char *argin)
{
	INFO_STREAM << "Starter::processCouldStart(\""<< argin << "\"): entering... !" << endl;

	//	Make sure that it's not running.
	if (servers.empty()==false)
	{
		string	name(argin);
		ControlledServer *server = util->get_server_by_name(name, servers);
		if (server!=NULL)
			if (server->get_state()!=Tango::FAULT)
			{
				INFO_STREAM << argin << " is already running !" <<endl;
				TangoSys_OMemStream tms;
				tms << argin << " is already running !" << ends;
				if (throwable)
					Tango::Except::throw_exception(
								(const char *)"ALREADY_RUNNING",
								tms.str(),
								(const char *)"Starter::dev_start()");
				return NULL;
			}
	}

	//	Separate server name and instanceName.
	char	*servname     = util->get_server_name(argin) ;
	char	*instancename = util->get_instance_name(argin);
	char	*adminname    = new char[strlen(servname)+ strlen(instancename)+10];
	sprintf(adminname, "dserver/%s/%s", servname, instancename);
	char	*filename;
	try {
		filename = util->check_exe_file(servname, startDsPath);
	}
	catch(Tango::DevFailed &e)
	{
		delete[] servname;
		delete[] instancename;
		delete[] adminname;
		if (throwable)
			throw;
		else
		{
			cerr << e.errors[0].desc << endl;
			return NULL;
		}
	}
	delete[] servname;

	check_log_dir();

	string	log_file = util->build_log_file_name(argin);
	NewProcess	*np  = new NewProcess;
	np->serverName     = filename;
	np->instanceName = instancename;
	np->adminName    = adminname;
	np->logFileName      = new char[log_file.length()+1];
	np->logFileName      = strcpy(np->logFileName, log_file.c_str());

	INFO_STREAM << "LOG file : " << log_file << endl;

	return np;
}
//+------------------------------------------------------------------
//+------------------------------------------------------------------
void Starter::startProcesses(vector<NewProcess *> v_np, int level)
{
	//	Start process to start processes
	try {
		start_proc_data->push_back_level(level);
		StartProcessThread	*thread = new StartProcessThread(v_np, level, this);
		thread->start();
	}
	catch(omni_thread_fatal &e) {
		TangoSys_OMemStream tms;
		tms << "Starting process thread failed: " << e.error;
		Tango::Except::throw_exception(
			   (const char *)"THREAD_FAILDE",
			   tms.str().c_str(),
			   (const char *)"Starter::startProcesses()");
	}
	catch(omni_thread_invalid &e) {
		TangoSys_OMemStream tms;
		tms << "Starting process thread failed: omni_thread_invalid";
		Tango::Except::throw_exception(
			   (const char *)"THREAD_FAILDE",
			   tms.str().c_str(),
			   (const char *)"Starter::startProcesses()");
	}
	catch(...) {
		TangoSys_OMemStream tms;
		tms << "Starting process thread failed";
		Tango::Except::throw_exception(
			   (const char *)"THREAD_FAILDE",
			   tms.str().c_str(),
			   (const char *)"Starter::startProcesses()");
	}
}
//+------------------------------------------------------------------
/**
 *	Return how many servers to start for specified level.
 */
//+------------------------------------------------------------------
int	Starter::nb_servers_to_start(int level)
{
	int	cnt = 0;
	for (unsigned int i=0 ; i<servers.size() ; i++)
	{
		ControlledServer *server = &servers[i];
		//	server->running could not be initialized
		if (server->controlled  &&  server->startup_level==level)
			if (server->get_state()!=Tango::ON)
				cnt++;
	}
	return cnt;
}
//=================================================================
//=================================================================
void Starter::check_host()
{
	string	hostname(Tango::Util::instance()->get_host_name().c_str());
	transform(hostname.begin(), hostname.end(), hostname.begin(), ::tolower);
	//	remove FQDN
	string::size_type	pos = hostname.find('.');
	if (pos!=string::npos)
		hostname = hostname.substr(0, pos);

	string	devname = device_name;
	transform(devname.begin(), devname.end(), devname.begin(), ::tolower);

	//	Get only member
	pos = devname.find('/');
	if (pos!=string::npos)
	{
		pos = devname.find('/', pos+1);
		if (pos!=string::npos)
			devname = devname.substr(pos+1);
	}
	//cout << hostname << " == " << devname << endl;

	if (devname != hostname)
	{
		TangoSys_OMemStream	tms;
		tms << "This server must run on " << devname << " and not on "  << hostname;
		string	descr(tms.str());

		Tango::Except::throw_exception(
				(const char *)"BAD_PARAM",
				descr.c_str(),
				(const char *)"Starter::check_host()");
	}
}
//=================================================================
//=================================================================
void Starter::check_log_dir()
{
	//	Check if log dir already exists.
	//-------------------------------------
	string	logpath;
	LogPath(logpath,logFileHome);
	if (chdir(logpath.c_str())==-1)
	{
		if (errno==ENOENT)
		{
			//	Create directory
			//-------------------------
			cerr << "ENOENT" << endl;
			cerr << errno << "  " << strerror(errno) << endl;
#ifdef _TG_WINDOWS_
			mkdir(TmpRoot);
			int r = mkdir(logpath.c_str());
#else
#	ifdef linux
			int r = mkdir(logpath.c_str(), (mode_t)(0775) );
#	else
			int r = mkdir(logpath.c_str(), (mode_t)(O_RDWR | O_CREAT, 0775) );
#	endif
#endif
			if (r<0)
			{
				TangoSys_OMemStream	message;
				message << "Cannot create error log directory:\n";
				message << logpath;
				message << "\n" << strerror(errno) << endl;
				cerr << message.str() << endl;;
				set_status(message.str());
				Tango::Except::throw_exception(
									(const char *)"CANNOT_CREATE_LOG_FILE",
									message.str(),
									(const char *)"Starter::dev_start");
			}
			else
			{
				TangoSys_OMemStream	tms;
				tms << logpath << " Created !" << endl;
				INFO_STREAM << tms.str() << endl;
				set_status(tms.str());
			}
		}
		else
		{
			TangoSys_OMemStream	tms;
			tms << "Cannot change to log directory:\n";
			tms << logpath;
			tms << "\n" << strerror(errno) << endl;
			cerr << tms.str() << endl;;
			set_status(tms.str());
		}
	}
}
//=================================================================
//=================================================================
void Starter::manage_changing_state(ControlledServer *server, TANGO_UNUSED(Tango::DevState previous_state))
{
	//	Do it only if server is controlled.
	if (server->controlled==false || server->startup_level==0)
		return;

	Tango::DevState state = server->get_state();
	//cout << "manage_changing_state:[" << server->name << "]	" <<
	//	Tango::DevStateName[previous_state]	<< "  -->  " << Tango::DevStateName[state] << endl;

	switch(state)
	{
	case Tango::ON:
		server->started_time = time(NULL);
		//	Log statistics
		util->log_starter_statistics(server);

		if (server->failure_time>0)
		{
			cout << "Failure duration:	" <<
				(server->started_time-server->failure_time)  << " sec." << endl;
			server->failure_time  = -1;
		}
		break;
	case Tango::FAULT:
		if (server->stopped==false)		//	Has failed
		{
			server->failure_time = time(NULL);
			//	Log statistics
			util->log_starter_statistics(server);

			//	Check auto restart
			if (autoRestartDuration>0) {
				int	minDuration = autoRestartDuration;

				if (debug==false)
					minDuration *= 60;	//	minutes to seconds
				time_t	runDuration = server->failure_time -  server->started_time;
				cout << "Has run " << runDuration << " sec.  (> " << minDuration << " ?)" << endl;
				if (runDuration>minDuration) {
					try {
						//	Restart it
						cout << "	YES:  Restart it !!" << endl;
						server->auto_start = true;
						dev_start((char *)server->name.c_str());
					}
					catch(Tango::DevFailed &e) {
						Tango::Except::print_exception(e);
					}
				}
			}
		}
		break;
	default:
		//	Do nothing
		break;
	}
}
	/*----- PROTECTED REGION END -----*/	//	Starter::namespace_ending
} //	namespace
