/*----- 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/tango.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "StarterUtil.h"
#include "Starter.h"
#include "StarterClass.h"

#include "Logging.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
//  ResetStatistics       |  reset_statistics
//  UpdateServersInfo     |  update_servers_info
//================================================================

//================================================================
//  Attributes managed are:
//================================================================
//  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, std::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
}
//--------------------------------------------------------
Starter::~Starter()
{
	delete_device();
}

//--------------------------------------------------------
/**
 *	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 << std::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
            std::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(nullptr);
            util->proc_util->join(nullptr);

            //	Delete device allocated objects
            delete dbase;
            delete util;
            delete[] attr_HostState_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 << std::endl;
	/*----- PROTECTED REGION ID(Starter::init_device_before) ENABLED START -----*/

        //	Initialization before get_device_property() call
        TANGO_LOG_INFO << "Starter::Starter() init device " << device_name << std::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 != nullptr)
            if (strcmp(dbg, "true") == 0) {
                debug = true;
                TANGO_LOG_INFO << "!!! Debug mode is set !!!" << std::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)
            //-------------------------------------------------------------
            std::vector<std::string> hosts_list;
            char *env = getenv("TANGO_CLUSTER");
            std::string host_name(tg->get_host_name());
            if (env == nullptr)
                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
                //--------------------------------------------------
                std::string str_list(env);
                TANGO_LOG_INFO << "hosts_list = " << str_list << std::endl;
                unsigned int start = 0;
                unsigned end = 0;
                while ((end = (int) str_list.find_first_of(':', (unsigned long) start)) > 0) {
                    std::string s = str_list.substr(start, end - start);
                    hosts_list.push_back(s);
                    start = end + 1;
                }
                std::string s = str_list.substr(start, str_list.length() - start);
                hosts_list.push_back(s);
                for (const auto & i : hosts_list)
                    TANGO_LOG_INFO << i << std::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];

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

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

            //	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 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 =
                    ((dynamic_cast<StarterClass *>(get_device_class()))->nbStartupLevels);

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

            //	Want std::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 !" << std::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("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 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.emplace_back(".");
        else
            for (unsigned long i = 0; i < startDsPath.size(); i++)
                INFO_STREAM << "startDsPath[" << i << "] = " << startDsPath[i] << std::endl;
        INFO_STREAM << "WaitForDriverStartup = " << waitForDriverStartup << " seconds" << std::endl;
        TANGO_LOG_INFO << "interStartupLevelWait  = " << interStartupLevelWait << std::endl;
        TANGO_LOG_INFO << "serverStartupTimeout   = " << serverStartupTimeout << std::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);
        std::string tmp;
        if (!data[0].is_empty())
            data[0] >> tmp;
        transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
        if (tmp == "false")
            fireFromDbase = false;
        TANGO_LOG_INFO << "fireFromDbase  = " << fireFromDbase << std::endl;
        TANGO_LOG_INFO << "logFileHome    = " << logFileHome << std::endl;
        TANGO_LOG_INFO << "StartServersAtStartup = " << startServersAtStartup << std::endl;
        TANGO_LOG_INFO << "AutoRestartDuration   = " << autoRestartDuration << std::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 << std::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(std::vector<long> &attr_list))
{
	DEBUG_STREAM << "Starter::read_attr_hardware(std::vector<long> &attr_list) entering... " << std::endl;
	/*----- PROTECTED REGION ID(Starter::read_attr_hardware) ENABLED START -----*/

        //	Update if Servers attribute (polled) is called.
        for (long i : attr_list) {
            Tango::WAttribute &att = dev_attr->get_w_attr_by_ind(i);
            std::string attr_name(att.get_name());
            if (attr_name == "Servers")
                for (auto & server : servers) {
                    Tango::DevState previous_state = server.get_state();
                    //	Update server state
                    server.set_state(server.thread_data->get_state());
                    server.nbInstances = server.thread_data->getNbInstaces();

                    //	Check if state has changed.
                    if (previous_state != server.get_state())
                        manage_changing_state(&server, previous_state);

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

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

//--------------------------------------------------------
/**
 *	Read attribute HostState related method
 *
 *
 *	Data type:	Tango::DevShort
 *	Attr type:	Scalar
 */
//--------------------------------------------------------
void Starter::read_HostState(Tango::Attribute &attr)
{
	DEBUG_STREAM << "Starter::read_HostState(Tango::Attribute &attr) entering... " << std::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] << std::endl;
        attr.set_value(attr_HostState_read);

    /*----- PROTECTED REGION END -----*/	//	Starter::read_HostState
}
//--------------------------------------------------------
/**
 *	Read attribute RunningServers related method
 *
 *
 *	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... " << std::endl;
	/*----- PROTECTED REGION ID(Starter::read_RunningServers) ENABLED START -----*/

        //	Check running ones
        std::vector<std::string> runnings;
        for (auto & server : servers)
            if (server.get_state() == Tango::ON)
                runnings.push_back(server.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... " << std::endl;
	/*----- PROTECTED REGION ID(Starter::read_StoppedServers) ENABLED START -----*/

        //	Check stopped ones
        std::vector<std::string> stopped;
        for (auto & server : servers)
            if (server.get_state() != Tango::ON)
                stopped.push_back(server.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... " << std::endl;
	/*----- PROTECTED REGION ID(Starter::read_Servers) ENABLED START -----*/

        //	Check starting ones
        std::vector<std::string> vs;
        for (auto & server : servers) {
            TangoSys_OMemStream tms;
            tms << server.name << '\t'
                << Tango::DevStateName[server.get_state()] << '\t'
                << server.controlled << '\t'
                << server.startup_level << '\t' << server.nbInstances;
            std::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 << std::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 =
                ((dynamic_cast<StarterClass *>(get_device_class()))->readInfoDbPeriod);

        //	If not fired -> do it myself by polling
        //---------------------------------------------
        if (!fireFromDbase) {
            static time_t t0 = 0;
            time_t t1 = time(nullptr);
            //	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" << std::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 if servers object initialized
        if (servers.empty()) {
            INFO_STREAM << "Exiting dev_state() with servers.size() nullptr" << std::endl;
            argout = Tango::ON;
        } 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 (ControlledServer server : servers) {
                p_serv = &server;
                //	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) {
                        //TANGO_LOG_INFO << p_serv->get_moving_duration() << std::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) {
                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 << std::endl;
	/*----- PROTECTED REGION ID(Starter::dev_start) ENABLED START -----*/
        try {
            NewProcess *np = processCouldStart(argin);
            if (np == nullptr)
                return;
            //	Build a std::vector to start process
            std::vector<NewProcess *> processes;
            processes.push_back(np);
            startProcesses(processes, 0);

            //	Started with starter -> stopped switched to false.
            std::string servname(argin);
            ControlledServer *server = util->get_server_by_name(servname, servers);
            if (server != nullptr) {
                server->stopped = false;
                server->started_time = time(nullptr);
            }
        }
        catch (Tango::DevFailed &) {
            throw;
        }
        catch (std::exception &e) {
            std::cerr << "================================" << std::endl;
            std::cerr << e.what() << std::endl;
            std::cerr << "================================" << std::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 (...) {
            std::cerr << "================================" << std::endl <<
                 "    Unknown std::exception caught" << std::endl <<
                 "================================" << std::endl;
            Tango::Except::throw_exception(
                    (const char *) "START_PROCASS_FAILDE",
                    (const char *) "Starting process failed:    Unknown std::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 << std::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 !" << std::ends;
            Tango::Except::throw_exception(out_stream.str(),
                                           out_stream.str(),
                                           (const char *) "Starter::dev_stop()");
            return;
        }

        //	Check Argin as server name
        //----------------------------------
        std::string name(argin);
        ControlledServer *server = util->get_server_by_name(name, servers);
        if (server == nullptr) {
            TangoSys_OMemStream out_stream;
            out_stream << argin << ": Unknown Server !" << std::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 = nullptr;
            try {
                dev = new Tango::DeviceProxy(server->admin_name);
                dev->command_inout("Kill");
                delete dev;
            }
            catch (Tango::DevFailed &e) {
                delete dev;
                throw e;
            }

            TangoSys_OMemStream out_stream;
            out_stream << argin << " stopped";
            WARN_STREAM << out_stream.str() << std::endl;
            TANGO_LOG_INFO << out_stream.str() << std::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 !" << std::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 !" << std::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 << std::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" << std::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 !" << std::ends;
                Tango::Except::throw_exception(out_stream.str(),
                                               out_stream.str(),
                                               (const char *) "Starter::dev_start_all()");
            }
        }

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

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

        //	Want std::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 << std::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 !" << std::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
        TANGO_LOG_INFO << "Starter removing level " << level << std::endl;
        start_proc_data->remove_level(level);

        //	And stop the running ones
        for (auto & i : servers) {
            ControlledServer *server = &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 << std::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... !" << std::endl;

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

    //	prepare the argout for running servers list
    //-----------------------------------------------------------
    unsigned long nb = 0;
    for (auto & server : servers)
        if (all_serv || server.controlled)
            if (server.get_state() == Tango::ON)
                nb++;

    //	And fill it
    //-----------------------------------------------------------
    argout->length((_CORBA_ULong) nb);
    for (unsigned long 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 << std::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 << std::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... !" << std::endl;

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

    //	prepeare the argout for NOT running servers list
    //-----------------------------------------------------------
    unsigned long nb = 0;
    for (auto & server : servers)
        if (all_serv || server.controlled)
            if (server.get_state() == Tango::FAULT)
                nb++;

    //	And fill it
    //-----------------------------------------------------------
    argout->length((_CORBA_ULong) nb);
    for (unsigned long i = 0, x = 0; i < servers.size() && x < nb; i++)
        if (all_serv || servers[i].controlled)
            if (servers[i].get_state() == Tango::FAULT) {
                INFO_STREAM << "STOPPED: " << servers[i].name << std::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 << std::endl;
	/*----- PROTECTED REGION ID(Starter::dev_read_log) ENABLED START -----*/

        //	Add your own code
        std::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
        std::ifstream ifs((char *) filename.c_str());
        if (!ifs) {
            //	Open log file failed -> Throw std::exception
            //----------------------------------------------
            TangoSys_OMemStream reason;
            TangoSys_OMemStream description;
            reason << "Cannot open " << filename << std::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 std::string read from it.
        //-------------------------------------------------------------
        std::stringstream strLog;
        if (!on_starter) {
            strLog << filename << std::endl;
            strLog << util->get_file_date((char *) filename.c_str()) << std::endl << std::endl;
        }
        strLog << ifs.rdbuf() << std::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 << std::endl;
	/*----- PROTECTED REGION ID(Starter::hard_kill_server) ENABLED START -----*/

        //	Add your own code
        std::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 = nullptr;				//- process addr (in the heap)
            if( (handle=OpenProcess(PROCESS_TERMINATE, false, pid)) == nullptr)
            {
                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 << " (pid " << pid << ") failed !";
                Tango::Except::throw_exception(
                            (const char *)"KILL_DERVER_FAILED",
                            tms.str().c_str(),
                            (const char *)"Starter::hard_kill_server()");
            }

#else

        int result = kill(pid, SIGKILL);
        if (result < 0) {
            TangoSys_OMemStream tms;
            tms << "Kill server " << argin << " (pid " << pid << ") failed !" << " " << strerror(errno);
            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 ResetStatistics related method
 *	Description: Reset statistics file.
 *
 */
//--------------------------------------------------------
void Starter::reset_statistics()
{
	DEBUG_STREAM << "Starter::ResetStatistics()  - " << device_name << std::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 << std::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... !" << std::endl;

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

        //	Separate server name and instanceName.
        char *servname = util->get_server_name(argin);
        char *instancename = util->get_instance_name(argin);
        const std::size_t sizeof_adminname{strlen(servname) + strlen(instancename) + 10};
        char *adminname = new char[sizeof_adminname];
        snprintf(adminname, sizeof_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 {
                std::cerr << e.errors[0].desc << std::endl;
                return nullptr;
            }
        }
        delete[] servname;

        check_log_dir();

        std::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 << std::endl;

        return np;
    }

//+------------------------------------------------------------------
//+------------------------------------------------------------------
    void Starter::startProcesses(std::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 (auto & i : servers) {
            ControlledServer *server = &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() {
        std::string hostname(Tango::Util::instance()->get_host_name());
        transform(hostname.begin(), hostname.end(), hostname.begin(), ::tolower);
        //	remove FQDN
        std::string::size_type pos = hostname.find('.');
        if (pos != std::string::npos)
            hostname = hostname.substr(0, pos);

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

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

        if (devname != hostname) {
            TangoSys_OMemStream tms;
            tms << "This server must run on " << devname << " and not on " << hostname;
            std::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.
        //-------------------------------------
        std::string logpath;
        LogPath(logpath, logFileHome)
        if (chdir(logpath.c_str()) == -1) {
            if (errno == ENOENT) {
                //	Create directory
                //-------------------------
                std::cerr << "ENOENT" << std::endl;
                std::cerr << errno << "  " << strerror(errno) << std::endl;
#ifdef _TG_WINDOWS_
                mkdir(TmpRoot);
                int r = mkdir(logpath.c_str());
#else
                int r = mkdir(logpath.c_str(), (mode_t) (0775));
#endif
                if (r < 0) {
                    TangoSys_OMemStream message;
                    message << "Cannot create error log directory:\n";
                    message << logpath;
                    message << "\n" << strerror(errno) << std::endl;
                    std::cerr << message.str() << std::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 !" << std::endl;
                    INFO_STREAM << tms.str() << std::endl;
                    set_status(tms.str());
                }
            } else {
                TangoSys_OMemStream tms;
                tms << "Cannot change to log directory:\n";
                tms << logpath;
                tms << "\n" << strerror(errno) << std::endl;
                std::cerr << tms.str() << std::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 || server->startup_level == 0)
            return;

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

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

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

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

                        if (!debug)
                            minDuration *= 60;    //	minutes to seconds
                        time_t runDuration = server->failure_time - server->started_time;
                        TANGO_LOG_INFO << "Has run " << runDuration << " sec.  (> " << minDuration << " ?)" << std::endl;
                        if (runDuration > minDuration) {
                            try {
                                //	Restart it
                                TANGO_LOG_INFO << "	YES:  Restart it !!" << std::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
