static const char *RcsId = "$Id: DevServ.cpp 13293 2009-04-07 10:53:56Z pascal_verdier $";
//+=============================================================================
//
// file :         SingleShotAO.cpp
//
// description :  C++ source for the SingleShotAO 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
//                SingleShotAO are implemented in this file.
//
// project :      TANGO Device Server
//
// $Author: pascal_verdier $
//
// $Revision: 13293 $
//
// $Revision: 13293 $
// $Date: 2009-04-07 12:53:56 +0200 (Tue, 07 Apr 2009) $
//
// SVN only:
// $HeadURL: $
//
// CVS only:
// $Source$
// $Log$
// Revision 3.5  2007/10/24 12:07:35  pascal_verdier
// Another spelling mistake correction
//
// Revision 3.4  2007/10/23 14:04:30  pascal_verdier
// Spelling mistakes correction
//
// Revision 3.3  2005/03/02 14:06:15  pascal_verdier
// namespace is different than class name.
//
// Revision 3.2  2004/11/08 11:33:16  pascal_verdier
// if device property not found in database, it takes class property value if exists.
//
// Revision 3.1  2004/09/06 09:27:05  pascal_verdier
// Modified for Tango 5 compatibility.
//
//
// copyleft :    Synchrotron SOLEIL 
//               L'Orme des merisiers - Saint Aubin
//               BP48 - 91192 Gif sur Yvette
//               FRANCE
//
//-=============================================================================
//
//  		This file is generated by POGO
//	(Program Obviously used to Generate tango Object)
//
//         (c) - Software Engineering Group - ESRF
//=============================================================================



//===================================================================
//
//	The following table gives the correspondence
//	between commands and method name.
//
//  Command name|  Method name
//	----------------------------------------
//  State   |  dev_state()
//  Status  |  dev_status()
//  Abort   |  abort()
//
//===================================================================


#include <tango.h>
#include <yat4tango/Logging.h>
#include <yat4tango/DeviceInfo.h>
#include <SingleShotAO.h>
#include <SingleShotAOClass.h>
#include "SingleShotAOTypesAndConsts.h"


namespace SingleShotAO_ns
{

//- check manager macro:
#define CHECK_MANAGER() \
	do \
	{ \
	if (! m_manager) \
	THROW_DEVFAILED("DEVICE_ERROR", \
	"request aborted - the manager isn't accessible ", \
	"SingleShotAO::check_manager"); \
} while (0)


//+----------------------------------------------------------------------------
//
// method : 		SingleShotAO::SingleShotAO(string &s)
// 
// description : 	constructor for simulated SingleShotAO
//
// in : - cl : Pointer to the DeviceClass object
//      - s : Device name 
//
//-----------------------------------------------------------------------------
SingleShotAO::SingleShotAO(Tango::DeviceClass *cl,string &s)
:TANGO_BASE_CLASS(cl,s.c_str())
{
	init_device();
}

SingleShotAO::SingleShotAO(Tango::DeviceClass *cl,const char *s)
:TANGO_BASE_CLASS(cl,s)
{
	init_device();
}

SingleShotAO::SingleShotAO(Tango::DeviceClass *cl,const char *s,const char *d)
:TANGO_BASE_CLASS(cl,s,d)
{
	init_device();
}


//+----------------------------------------------------------------------------
//
// method : 		SingleShotAO::delete_device()
// 
// description : 	will be called at device destruction or at init command.
//
//-----------------------------------------------------------------------------
void SingleShotAO::delete_device()
{
	DEBUG_STREAM << "SingleShotAO::delete_device(): entering... !" << endl;

	// Make sure running processes are aborted properly if channels are running
	//--------------------------------------------
	try
	{
		_abort();
	}
	catch (Tango::DevFailed &df)
	{
		ERROR_STREAM << df << endl;
		RETHROW_DEVFAILED(df,
						  "DRIVER_FAILURE",
						  "could not abort [caught Tango::DevFailed]",
						  "SingleShotAO::delete_device");
	}
	catch (...)
	{
		ERROR_STREAM << "SingleShotAO::init::unknown exception caught" << std::endl;
		THROW_DEVFAILED("DRIVER_FAILURE",
						"could not abort [unknown error]",
						"SingleShotAO::delete_device");
	}

	// Delete device allocated objects
	//--------------------------------------------
	yat4tango::TraceHelper t("SingleShotAO::delete_device", this);

	if (m_manager)
	{
		m_manager->exit();
		m_manager = NULL;
	}

	// Release the asl::SingleShotAO object
	//--------------------------------------------
	if (m_ssao) 
	{
		delete m_ssao;
		m_ssao = NULL;
	}

	// Remove dynamic attributes
	//--------------------------------------------
	if (m_dyn_attr_manager)
	{
		try
		{
			m_dyn_attr_manager->remove_attributes();
		}
		catch (...)
		{
			//- ignore any error
		}
	}

	// delete dynamic attributes manager
	if (m_dyn_attr_manager)
	{
		delete m_dyn_attr_manager;
		m_dyn_attr_manager = NULL;
	}

	// Remove the inner appender
	//--------------------------------------------
	yat4tango::Logging::release(this);
	yat4tango::DeviceInfo::release(this);
}


//+----------------------------------------------------------------------------
//
// method : 		SingleShotAO::init_device()
// 
// description : 	will be called at device initialization.
//
//-----------------------------------------------------------------------------
void SingleShotAO::init_device()
{
	//- initialize the inner appender (first thing to do)
	try
	{
		INFO_STREAM << "Create the InnerAppender & FileAppender in order to manage logs." << endl;
		yat4tango::Logging::initialize(this);
		
		INFO_STREAM << "Create the DeviceInfo in order to manage info on versions." << endl;
		yat4tango::DeviceInfo::initialize(this, YAT_XSTR(PROJECT_NAME), YAT_XSTR(PROJECT_VERSION));
		yat4tango::DeviceInfo::add_dependency(this, "ASL", ">=1.0");
	}
	catch (Tango::DevFailed &df)
	{
		ERROR_STREAM << df << std::endl;
		m_state = Tango::FAULT;
		m_currStatus = "initialization failed - could not instantiate the InnerAppender";
		return;
	}

	INFO_STREAM << "Initializing device";

	//- trace/profile this method
	yat4tango::TraceHelper t("SingleShotAO::init_device", this);

	// construct the AO manager
	//--------------------------------------------
	try
	{
		m_manager = new SingleShotAOManager(this);
	}
	catch (...)
	{
		ERROR_STREAM << "initialization failed - failed to create manager" << std::endl;
		m_currStatus = "initialization failed [failed to create manager]. See log for details";
		m_state = Tango::FAULT;
		return;
	}

	// Initialise variables to default values
	//--------------------------------------------

	//- init members
	m_ssao = NULL;
	critical_properties_missing = false;
	isInitOk = false;
	m_frequency = 0.0;
	m_dyn_attr_manager = NULL;
	m_state = Tango::INIT;

	// Initialise variables to default values
	//--------------------------------------------
	try 
	{
		get_device_property();
	}
	catch (const Tango::DevFailed& df)
	{
		ERROR_STREAM << "SingleShotAO::init_device::Tango::DevFailed exception caught "
			<< "while trying to read device properties from TANGO database"
			<< std::endl;
		std::string error_msg = df.errors[0].desc;
		std::istringstream error_stream(error_msg);
		std::string line;
		std::getline(error_stream, line); // Read the first line
		ERROR_STREAM << line << std::endl;
		while (std::getline(error_stream, line)) { // Read the rest of the lines
			ERROR_STREAM << " - " << line << std::endl;
		}
		m_currStatus = error_msg;
		m_state = Tango::FAULT;
		return;
	}
	catch (...)
	{
		//- update internal state
		ERROR_STREAM << "SingleShotAO::init_device::unknown exception caught "
			<< "while trying to read device properties from TANGO database"
			<< std::endl;
		m_currStatus = "Failed to get property. See log for details";
		m_state = Tango::FAULT;
		return;
	}

	//- abort initialization if properties missing
	if (critical_properties_missing) 
	{
		ERROR_STREAM << "configuration error - unspecified or invalid device properties";
		m_currStatus = "configuration error [unspecified or invalid device properties]. See log for details";
		m_state = Tango::FAULT;
		return;
	}

	//- allocate the asl::SingleShotAO object
	m_ssao = new asl::SingleShotAO;
	if (m_ssao == 0) 
	{
		m_currStatus = "Failed to create the SignleShotAO. See log for details";
		m_state = Tango::FAULT;
		return;
	}

	try
	{
		//- initialze the hardware
		m_ssao->init(boardTypeId, boardNum);
	}
	catch(const asl::DAQException& de)
	{
		ERROR_STREAM << daq_to_tango_exception(de) << endl;
		m_currStatus = "Failed to init the SignleShotAO. See log for details";
		m_state = Tango::FAULT;
		return;
	}
	catch(...)
	{
		ERROR_STREAM << "SingleShotAO::init_device::unknown exception caught"<<std::endl;
		m_currStatus = "Failed to init the SignleShotAO. See log for details";
		m_state = Tango::FAULT;
		return;
	}

  	// initialize channel number according to board type
	m_nb_chan = 0;
	if (boardType == k6208_BOARD_TYPE)
	{
		m_nb_chan = 8;
	}
	if (boardType == k6216_BOARD_TYPE)
	{
		m_nb_chan = 16;
	}

	INFO_STREAM << "Board has " << m_nb_chan << " channels" << endl;

	// test the manager
	if (!m_manager)
	{
		ERROR_STREAM << "initialization failed - the manager is not created" << std::endl;
		m_currStatus = "initialization failed [the manager is not created]. See log for details";
		m_state = Tango::FAULT;
		return;
	}


	// get frequency value in database
	//--------------------------------------------
	try
	{
		m_frequency = yat4tango::PropertyHelper::get_memorized_attribute<double>(this, "frequency");
		DEBUG_STREAM << "Frequency : " << m_frequency << endl;
	}
	catch (...)
	{
		DEBUG_STREAM << "No memorized value found for frequency. Defaulting to " << m_frequency << "." << std::endl;
	}


	// initialize the AO manager
	//--------------------------------------------
	try
	{
		m_manager->init(m_ssao, m_nb_chan, m_frequency, enableRamps);
	}
	catch (Tango::DevFailed & df)
	{
		ERROR_STREAM << df << std::endl;
		m_currStatus = "initialization failed - Manager initialization failed [see device log for details]";
		m_state = Tango::FAULT;
		return;
	}
	catch (...)
	{
		ERROR_STREAM << "initialization failed - failed to initialize manager" << std::endl;
		m_currStatus = "initialization failed [failed to initialize manager]. See log for details";
		m_state = Tango::FAULT;
		return;
	}

	// Remove existing dynamic attributes to avoid duplicates or other issues
	// --------------------------------------------
	if (m_dyn_attr_manager)
	{
		try
		{
			m_dyn_attr_manager->remove_attributes();
			delete m_dyn_attr_manager;
			m_dyn_attr_manager = NULL;
			DEBUG_STREAM << "Existing dynamic attributes manager cleaned up" << endl;
		} 
		catch (...)
		{
			ERROR_STREAM << "Error cleaning up existing dynamic attributes manager, continuing..." << endl;
			// Continue anyway - we'll create a new one
		}
	}

	// Create dynamic attributes
	//--------------------------------------------

  	// create dynamic attribute manager
	try
	{
		m_dyn_attr_manager = new yat4tango::DynamicAttributeManager(this);
	}
	catch (Tango::DevFailed &e)
	{
		ERROR_STREAM << e << std::endl;
		m_currStatus = "Failed to create Dynamic Attribute Manager. See log for details";
		m_state = Tango::FAULT;
		return;
	}
	catch (...)
	{
		ERROR_STREAM << "Failed to create Dynamic Attribute Manager" << std::endl;
		m_currStatus = "Failed to create Dynamic Attribute Manager. See log for details";
		m_state = Tango::FAULT;
		return;
	}


	// add dynamic attributes: channel, speed & initial for each channel
  	std::vector<yat4tango::DynamicAttributeInfo> l_dynAttrList;
	
	// Log how many attributes we're going to create
	INFO_STREAM << "Creating dynamic attributes for " << m_nb_chan << " channels.";
	INFO_STREAM << "Total attributes: " << m_nb_chan * (enableRamps ? 3 : 1) << endl;

	for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++)
	{
		yat::OSStream oss;
		oss << l_cpt;

		// ╔═══════════════╗
		// ║ Channel value ║
		// ╚═══════════════╝
		yat4tango::DynamicAttributeInfo dai_channel;
		dai_channel.dev = this;
		dai_channel.tai.name = kCHANNEL + oss.str();
		dai_channel.tai.label = kCHANNEL + oss.str();

		//- describe the dyn attr we want...
		dai_channel.tai.data_type = Tango::DEV_DOUBLE;
		dai_channel.tai.data_format = Tango::SCALAR;
		dai_channel.tai.writable = Tango::READ_WRITE; // soso
		dai_channel.tai.disp_level = Tango::OPERATOR;
		dai_channel.tai.unit = "V";
		dai_channel.tai.standard_unit = "V";
		dai_channel.tai.display_unit = "V";
		dai_channel.tai.max_value = "10.0";
		dai_channel.tai.min_value = "-10.0";
		dai_channel.tai.description = "Output value for channel " + oss.str() + " (in measurementUnit).";
		dai_channel.tai.format = "%5.3f";
		dai_channel.cdb = false;

		//- read callback
		dai_channel.rcb = yat4tango::DynamicAttributeReadCallback::instanciate(const_cast<SingleShotAO&>(*this), 
			&SingleShotAO::read_channel);

		//- write callback
		dai_channel.wcb = yat4tango::DynamicAttributeWriteCallback::instanciate(const_cast<SingleShotAO&>(*this), 
			&SingleShotAO::write_channel);

		l_dynAttrList.push_back(dai_channel);

		// if enableRamps if false, skip speed and initial attributes
		if (!enableRamps)
		{
			DEBUG_STREAM << "Ramps are disabled. Skipping speed and initial attributes for channel " << l_cpt << std::endl;
			continue;
		}

		// ╔═══════════════╗
		// ║  Speed value  ║
		// ╚═══════════════╝
		yat4tango::DynamicAttributeInfo dai_speed;
		dai_speed.dev = this;
		dai_speed.tai.name = kSPEED + oss.str();
		dai_speed.tai.label = kSPEED + oss.str();

		//- describe the dyn attr we want...
		dai_speed.tai.data_type = Tango::DEV_DOUBLE;
		dai_speed.tai.data_format = Tango::SCALAR;
		dai_speed.tai.writable = Tango::READ_WRITE;
		dai_speed.tai.disp_level = Tango::OPERATOR;
		dai_speed.tai.unit = "V/s";
		dai_speed.tai.standard_unit = "V/s";
		dai_speed.tai.display_unit = "V/s";
		dai_speed.tai.description = "Speed for ramp generation, in V/s. If speed is NULL, no ramp generated but direct write on channel output " + oss.str() + " (in measurementUnit).";
		dai_speed.tai.format = "%5.3f";

		//- cleanup tango db option: cleanup tango db when removing this dyn. attr. (i.e. erase its properties from db)
		dai_speed.cdb = false;

		//- read callback
		dai_speed.rcb = yat4tango::DynamicAttributeReadCallback::instanciate(const_cast<SingleShotAO&>(*this), 
			&SingleShotAO::read_speed);

		//- write callback
		dai_speed.wcb = yat4tango::DynamicAttributeWriteCallback::instanciate(const_cast<SingleShotAO&>(*this), 
			&SingleShotAO::write_speed);
		
    	l_dynAttrList.push_back(dai_speed);
		

		// ╔═══════════════╗
		// ║ Initial value ║
		// ╚═══════════════╝
		yat4tango::DynamicAttributeInfo dai_initial;
		dai_initial.dev = this;
		dai_initial.tai.name = kINITIAL + oss.str();
		dai_initial.tai.label = kINITIAL + oss.str();

		//- describe the dyn attr we want...
		dai_initial.tai.data_type = Tango::DEV_DOUBLE;
		dai_initial.tai.data_format = Tango::SCALAR;
		dai_initial.tai.writable = Tango::READ_WRITE;
		dai_initial.tai.disp_level = Tango::OPERATOR;
		dai_initial.tai.unit = "V";
		dai_initial.tai.standard_unit = "V";
		dai_initial.tai.display_unit = "V";
		dai_initial.tai.description = "Initial value for ramp function, in V. Defaults to last written value in channel attribute " + oss.str() + ".";
		dai_initial.tai.format = "%5.3f";

		//- cleanup tango db option: cleanup tango db when removing this dyn. attr. (i.e. erase its properties from db)
		dai_initial.cdb = false;

		//- read callback
		dai_initial.rcb = yat4tango::DynamicAttributeReadCallback::instanciate(const_cast<SingleShotAO&>(*this), 
			&SingleShotAO::read_initial);

		//- write callback
		dai_initial.wcb = yat4tango::DynamicAttributeWriteCallback::instanciate(const_cast<SingleShotAO&>(*this), 
			&SingleShotAO::write_initial);
		
    	l_dynAttrList.push_back(dai_initial);
	}
	
	// Log the size of our attribute list before adding to manager
	INFO_STREAM << "Prepared " << l_dynAttrList.size() << " dynamic attributes for creation" << endl;
	
	// Add all attributes
	try 
	{
  		m_dyn_attr_manager->add_attributes(l_dynAttrList);
		INFO_STREAM << "Successfully added all dynamic attributes" << endl;
	}
	catch (Tango::DevFailed &df) 
	{
		ERROR_STREAM << "Failed to add dynamic attributes: " << df << endl;
		m_currStatus = "Failed to add dynamic attributes. See log for details";
		m_state = Tango::FAULT;
		return;
	}
	catch (...) 
	{
		ERROR_STREAM << "Unknown exception when adding dynamic attributes" << endl;
		m_currStatus = "Failed to add dynamic attributes. Unknown error";
		m_state = Tango::FAULT;
		return;
	}

	// Initialize maps in manager class for all channels
	try
	{
		for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++)
		{
			// Initialize with default values
			m_manager->set_channel(l_cpt, 0.0);
			if (enableRamps)
			{
				m_manager->set_speed(l_cpt, 0.0);
				m_manager->set_initial(l_cpt, 0.0);
			}
		}
	}
	catch (Tango::DevFailed &df)
	{
		ERROR_STREAM << "Failed to initialize channel maps: " << df << endl;
		m_currStatus = "Failed to initialize channel maps. See log for details";
		m_state = Tango::FAULT;
		return;
	}

	// Get memorized values from database
	for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++)
	{
		yat::OSStream oss;
		oss << l_cpt;

		// Helper function to get and set memorized attributes
		auto applyMemorizedAttr = [&](const std::string& attrPrefix, 
			void (SingleShotAOManager::*setter)(yat::uint16, double))
		{
			std::string attrName = attrPrefix + oss.str();
			try
			{
				double val = yat4tango::PropertyHelper::get_memorized_attribute<double>(this, attrName);
				DEBUG_STREAM << "Found memorized value for " << attrName << ": " << val << endl;

				// Write the value to the manager (that contains the "read" attributes values)
				(m_manager->*setter)(l_cpt, val);

				// Write the value to the "write" attributes using helper function
				setDynamicAttributeWriteValue(attrName, val);
			}
			catch (...)
			{
				DEBUG_STREAM << "No memorized value found for " << attrName << ". Setting default value." << std::endl;
			}
		};

		// Get and set memorized values for speed, initial and channel
		if (enableRamps)
		{
			applyMemorizedAttr(kSPEED, &SingleShotAOManager::set_speed);
			applyMemorizedAttr(kINITIAL, &SingleShotAOManager::set_initial); 
		}

		if (outputMemorizedChannelsAtInit)
		{
			applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::write_channel_direct); // write memorized value to board output (directly, without ramp)
		} else {
			applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::set_channel); // only apply memorized value to the device
		}
    }

	//- GO for task
	try
	{
		m_manager->go();
	}
	catch (Tango::DevFailed & df)
	{
		ERROR_STREAM << df << std::endl;
		m_currStatus = "initialization failed [failed to go for manager]";
		m_state = Tango::FAULT;
		return;
	}
	catch (...)
	{
		ERROR_STREAM << "initialization failed - failed to go for manager" << std::endl;
		m_currStatus = "initialization failed [failed to go for manager]. See log for details";
		m_state = Tango::FAULT;
		return;
	}

	//- update internal state
	m_state = Tango::ON;
	m_currStatus = "Device ready to execute AO request.";
	isInitOk = true;
}

//+------------------------------------------------------------------
/**
 * method : 		SingleShotAO::get_device_property()
 *
 * description : 	Read the device properties from database.
 *
 * @throws DevFailed if any critical property is missing, not set, or invalid
 */
void SingleShotAO::get_device_property()
{
	//	Read device properties from database
	//------------------------------------------------------------------
	Tango::DbData	dev_prop;
	dev_prop.push_back(Tango::DbDatum("BoardNum"));
	dev_prop.push_back(Tango::DbDatum("BoardType"));
	dev_prop.push_back(Tango::DbDatum("EnableRamps"));
	dev_prop.push_back(Tango::DbDatum("OutputMemorizedChannelsAtInit"));


	// Call database and extract values
	//--------------------------------------------
	if (Tango::Util::instance()->_UseDb==true)
	{
		get_db_device()->get_property(dev_prop);
	}

	Tango::DbDatum def_prop, cl_prop;
	SingleShotAOClass *ds_class = (static_cast<SingleShotAOClass *>(get_device_class()));
	int	i = -1;

	//- <BoardNum> -----------------------
	// Try to initialize BoardNum from class property
	cl_prop = ds_class->get_class_property(dev_prop[++i].name);
	if (cl_prop.is_empty()==false)	cl_prop  >>  boardNum;
	else {
		// Try to initialize BoardNum from default device value
		def_prop = ds_class->get_default_device_property(dev_prop[i].name);
		if (def_prop.is_empty() == false)
		{
			def_prop >> boardNum_default;
			def_prop >> boardNum;
		}
	}
	// And try to extract BoardNum value from database
	if (dev_prop[i].is_empty() == false) dev_prop[i] >> boardNum;
	INFO_STREAM << "Raw BoardNum parsed: " << boardNum << endl;

	//- <BoardType> -----------------------
	// Try to initialize BoardType from class property
	cl_prop = ds_class->get_class_property(dev_prop[++i].name);
	if (cl_prop.is_empty() == false) cl_prop >> boardType;
	else {
		// Try to initialize BoardType from default device value
		def_prop = ds_class->get_default_device_property(dev_prop[i].name);
		if (def_prop.is_empty() == false)
		{
			def_prop >> boardType_default;
			def_prop >> boardType;
		}
	}
	// And try to extract BoardType value from database
	if (dev_prop[i].is_empty() == false) dev_prop[i] >> boardType;
	INFO_STREAM << "Raw BoardType parsed: " << boardType << endl;

	//- <EnableRamps> -----------------------
	//	Try to initialize EnableRamps from class property
	cl_prop = ds_class->get_class_property(dev_prop[++i].name);
	if (cl_prop.is_empty() == false) cl_prop >> enableRamps;
	else {
		//	Try to initialize EnableRamps from default device value
		def_prop = ds_class->get_default_device_property(dev_prop[i].name);
		if (def_prop.is_empty() == false)
		{
			def_prop >> enableRamps_default;
			def_prop >> enableRamps;
		}
	}
	//	And try to extract EnableRamps value from database
	if (dev_prop[i].is_empty() == false) dev_prop[i] >> enableRamps;
	INFO_STREAM << "EnableRamps parsed: " << (enableRamps ? "true" : "false") << endl;

	//- <OutputMemorizedChannelsAtInit> -----------------------
	//	Try to initialize OutputMemorizedChannelsAtInit from class property
	cl_prop = ds_class->get_class_property(dev_prop[++i].name);
	if (cl_prop.is_empty() == false) cl_prop >> outputMemorizedChannelsAtInit;
	else {
		//	Try to initialize OutputMemorizedChannelsAtInit from default device value
		def_prop = ds_class->get_default_device_property(dev_prop[i].name);
		if (def_prop.is_empty() == false)
		{
			def_prop >> outputMemorizedChannelsAtInit_default;
			def_prop >> outputMemorizedChannelsAtInit;
		}
	}
	//	And try to extract OutputMemorizedChannelsAtInit value from database
	if (dev_prop[i].is_empty() == false) dev_prop[i] >> outputMemorizedChannelsAtInit;
	INFO_STREAM << "OutputMemorizedChannelsAtInit parsed: " << (outputMemorizedChannelsAtInit ? "true" : "false") << endl;


	// Create properties if empty and set default values
	//--------------------------------------------
	DEBUG_STREAM << "Creating properties if empty" << endl;
	yat4tango::PropertyHelper::create_property_if_empty(this, dev_prop, boardNum_default, "BoardNum");
	yat4tango::PropertyHelper::create_property_if_empty(this, dev_prop, boardType_default, "BoardType");
	yat4tango::PropertyHelper::create_property_if_empty(this, dev_prop, enableRamps_default, "EnableRamps");
	yat4tango::PropertyHelper::create_property_if_empty(this, dev_prop, outputMemorizedChannelsAtInit_default, "OutputMemorizedChannelsAtInit");


	// Check critical properties being valid
	//--------------------------------------------
	DEBUG_STREAM << "Checking if critical properties are valid:" << endl;
	critical_properties_missing = false;
	yat::OSStream errorMessages;
	errorMessages << "Critical properties are missing or invalid:" << endl;

	//- <BoardNum> -----------------------
	// Case 1 (error): boardNum is set to the default value in the database
	if (boardNum == boardNum_default)
	{
		errorMessages << "Device property <BoardNum> is not set (default value " << boardNum_default << " needs to be replaced)" << endl;
		critical_properties_missing = true;
	}
	// Case 2 (error): boardNum is set to a value outside the valid range
	else if (boardNum < 0 || boardNum > 7)
	{
		boardNum = 0;
		errorMessages << "Device property <BoardNum> is invalid. Valid range is [0..7]" << endl;
		critical_properties_missing = true;
	}
	// Case 3 (ok): boardNum is set to a valid value
	else
	{
		INFO_STREAM << "BoardNum resolved to " << boardNum << endl;
	}

	//- <BoardType> -----------------------
	// Case 1 (error): boardType is set to the default value in the database
	if (boardType == boardType_default || boardType == "")
	{
		errorMessages << "Device property <BoardType> is not set (default value " << boardType_default << " needs to be replaced)" << endl;
		critical_properties_missing = true;
	}
	// Case 2 (ok): boardType is set to a valid 
	else if (boardType == "MAO_6208")
	{
		boardType = k6208_BOARD_TYPE;
		boardTypeId = adl::PCI6208;
		INFO_STREAM << "BoardType resolved to PCI6208" << endl;
	}
	else if (boardType == "MAO_6216")
	{
		boardType = k6216_BOARD_TYPE;
		boardTypeId = adl::PCI6216;
		INFO_STREAM << "BoardType resolved to PCI6216" << endl;
	}
	// Case 3 (error): boardType is set to an invalid value
	else
	{
		boardType = kDEFAULT_BOARD_TYPE;
		boardTypeId = adl::PCI6208;
		errorMessages << "Device property <BoardType> is invalid [supported hw: MAO_6208 or MAO_6216]" << endl;
		critical_properties_missing = true;
	}


	// If any critical property is missing or invalid, throw an exception
	if (critical_properties_missing)
	{
		DEBUG_STREAM << "Critical properties are missing, throwing exception." << endl;
		THROW_DEVFAILED("DEVICE_ERROR", errorMessages.str().c_str(), "SingleShotAO::get_device_property");
	}
	else
	{
		DEBUG_STREAM << "All critical properties are valid." << endl;
	}
}


//+----------------------------------------------------------------------------
//
// method : 		SingleShotAO::always_executed_hook()
// 
// description : 	method always executed before any command is executed
//
//-----------------------------------------------------------------------------
void SingleShotAO::always_executed_hook()
{
	// nothing to do
}


//+----------------------------------------------------------------------------
//
// method : 		SingleShotAO::read_attr_hardware
// 
// description : 	Hardware acquisition for attributes.
//
//-----------------------------------------------------------------------------
void SingleShotAO::read_attr_hardware(vector<long> &attr_list)
{
	// nothing to do
}


//+----------------------------------------------------------------------------
//
// method : 		SingleShotAO::read_frequency
// 
// description : 	Extract real attribute values for Frequency acquisition result.
//
//-----------------------------------------------------------------------------
void SingleShotAO::read_frequency(Tango::Attribute &attr)
{
	attr.set_value(&m_frequency);
}


//+----------------------------------------------------------------------------
//
// method : 		SingleShotAO::write_frequency
// 
// description : 	Write Frequency attribute values to hardware.
//
//-----------------------------------------------------------------------------
void SingleShotAO::write_frequency(Tango::WAttribute &attr)
{
	DEBUG_STREAM << "SingleShotAO::write_frequency(Tango::WAttribute &attr) entering... "<< endl;
	if (dev_state() != Tango::ON)
	{
		THROW_DEVFAILED("DEVICE_ERROR",
			"could not write frequency. The state must be ON.",
			"SingleShotAO::write_frequency");
	}
	CHECK_MANAGER();
	attr.get_write_value(m_frequency);
	m_manager->write_frequency(m_frequency);
	yat4tango::PropertyHelper::set_memorized_attribute(this, "frequency", m_frequency);
}


//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::dev_state
*
*  description:  method to execute "State"
*  This command gets the device state (stored in its <i>device_state</i> data member) and returns it to the caller.
*
* @return  State Code
*
*/
//+------------------------------------------------------------------
Tango::DevState SingleShotAO::dev_state()
{ 
	if (isInitOk)
	{
		CHECK_MANAGER();
		m_state = m_manager->get_state();
	}
	return m_state;
}


//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::dev_status
*
*  description:  method to execute "Status"
*  This command gets the device status (stored in its <i>device_status</i> data member) and returns it to the caller.
*
* @return  Status description
*
*/
//+------------------------------------------------------------------
Tango::ConstDevString SingleShotAO::dev_status()
{
	static std::string s = "";
	yat::OSStream oss;
	if (isInitOk)
	{
		CHECK_MANAGER();
		oss << m_manager->get_status() << std::endl;
	}
	else
	{
		oss << m_currStatus.c_str() << std::endl;
	}
	s = oss.str();
	return s.c_str();
}


//+------------------------------------------------------------------
/**
 * Extract the number from a dynamic attribute name with a specific prefix.
 * Used for parsing dynamic attribute names like "channelX", "speedX", "initialX"
 * where X is the channel number.
 * 
 * @param str The input string to search for numbers
 * @param prefix The expected prefix (e.g., "channel", "speed", "initial")
 * @return The number found after the prefix
 * @throws DevFailed if string doesn't start with prefix or no number is found
 */
int extractNumber(const std::string &str, const char* prefix) 
{
	// Validate string starts with the given prefix
	if (str.find(prefix) != 0)
	{
		THROW_DEVFAILED("DEVICE_ERROR",
			"String must start with the expected prefix",
			"SingleShotAO::extractNumber");
	}

	// Find first digit after the prefix
	size_t pos = strlen(prefix);

	// Extract and convert the number 
	std::string numberStr = str.substr(pos);
	if (numberStr.empty() || !isdigit(numberStr[0]))
	{
		THROW_DEVFAILED("DEVICE_ERROR",
			"No number found after prefix",
			"SingleShotAO::extractNumber");
	}

	return std::stoi(numberStr);
}

//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::read_channel
*
*  description:  method to execute "read_channel" for dynamic attributes
*
*/
//+------------------------------------------------------------------
void SingleShotAO::read_channel(yat4tango::DynamicAttributeReadCallbackData & cbd)
{
	yat::AutoMutex<> guard(m_lock);
	std::string l_attr_name = cbd.dya->get_name();
	yat::uint16 l_idx = extractNumber(l_attr_name, kCHANNEL); // extract channel nb

    CHECK_MANAGER();
	double l_val = m_manager->get_channel(l_idx);
	cbd.tga->set_value(&l_val);
}


//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::write_channel
*
*  description:  method to execute "write_channel" for dynamic attributes
*
*/
//+------------------------------------------------------------------
void SingleShotAO::write_channel(yat4tango::DynamicAttributeWriteCallbackData & cbd)
{
	double l_val;
	cbd.tga->get_write_value(l_val);
	cbd.tga->set_write_value(l_val);

	std::string l_attr_name = cbd.dya->get_name();
	yat::uint16 l_idx = extractNumber(l_attr_name, kCHANNEL); // extract channel nb

    CHECK_MANAGER();
	try
	{
		m_manager->write_channel(l_idx, l_val);
	}
	catch(Tango::DevFailed& df)
	{
		ERROR_STREAM << df<< endl;
		RETHROW_DEVFAILED(df,
			"DRIVER_FAILURE",
			"could not write channel [caught Tango::DevFailed]",
			"SingleShotAO::write_channel");
	}
	catch(...)
	{
		ERROR_STREAM << "SingleShotAOManager::write_channel::unknown exception caught"<<std::endl;
		THROW_DEVFAILED("DRIVER_FAILURE",
			"could not write channel [unknown error]",
			"SingleShotAO::write_channel");
	}
	DEBUG_STREAM << "SingleShotAO::write_channel(): channel " << l_idx << " value set to " << l_val << endl;

	yat4tango::PropertyHelper::set_memorized_attribute(this, l_attr_name, l_val);
	
	// Update the initial attribute to match the channel value
	std::string initialAttrName = kINITIAL + std::to_string(l_idx);
	yat4tango::PropertyHelper::set_memorized_attribute(this, initialAttrName, l_val);
	setDynamicAttributeWriteValue(initialAttrName, l_val);
	
	DEBUG_STREAM << "SingleShotAO::write_channel(): memorized attribute " << l_attr_name << " set to " << l_val << endl;
}


//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::read_speed
*
*  description:  method to execute "read_speed" for dynamic attributes
*
*/
//+------------------------------------------------------------------
void SingleShotAO::read_speed(yat4tango::DynamicAttributeReadCallbackData & cbd)
{
	yat::AutoMutex<> guard(m_lock);
	std::string l_attr_name = cbd.dya->get_name();
	yat::uint16 l_idx = extractNumber(l_attr_name, kSPEED); // extract channel nb

    CHECK_MANAGER();
	double l_val = m_manager->get_speed(l_idx);
	cbd.tga->set_value(&l_val);
}


//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::write_speed
*
*  description:  method to execute "write_speed" for dynamic attributes
*
*/
//+------------------------------------------------------------------
void SingleShotAO::write_speed(yat4tango::DynamicAttributeWriteCallbackData & cbd)
{
	DEBUG_STREAM << "SingleShotAO::write_speed(): entering... !" << endl;

	double l_val;
	cbd.tga->get_write_value(l_val);

	std::string l_attr_name = cbd.dya->get_name();
	yat::uint16 l_idx = extractNumber(l_attr_name, kSPEED); // extract channel nb
	if (l_val < 0)
	{
		l_val = -l_val;
	}

	CHECK_MANAGER();
	try
	{
		m_manager->set_speed(l_idx, l_val);
	}
	catch(Tango::DevFailed& df)
	{
		ERROR_STREAM << df<< endl;
		RETHROW_DEVFAILED(df,
			"DRIVER_FAILURE",
			"could not write speed [caught Tango::DevFailed]",
			"SingleShotAO::write_speed");
	}
	catch(...)
	{
		ERROR_STREAM << "SingleShotAO::write_speed::unknown exception caught" << std::endl;
		THROW_DEVFAILED("DRIVER_FAILURE",
			"could not write speed [unknown error]",
			"SingleShotAO::write_speed");
	}

	yat4tango::PropertyHelper::set_memorized_attribute(this, l_attr_name, l_val);
}


//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::read_initial
*
*  description:  method to execute "read_initial" for dynamic attributes
*
*/
//+------------------------------------------------------------------
void SingleShotAO::read_initial(yat4tango::DynamicAttributeReadCallbackData & cbd)
{
	yat::AutoMutex<> guard(m_lock);
	std::string l_attr_name = cbd.dya->get_name();
	yat::uint16 l_idx = extractNumber(l_attr_name, kINITIAL); // extract channel nb

	CHECK_MANAGER();
	double l_val = m_manager->get_initial(l_idx);
	cbd.tga->set_value(&l_val);
}


//+------------------------------------------------------------------
/**
*  method:  SingleShotAO::write_initial
*
*  description:  method to execute "write_initial" for dynamic attributes
*
*/
//+------------------------------------------------------------------
void SingleShotAO::write_initial(yat4tango::DynamicAttributeWriteCallbackData & cbd)
{
	DEBUG_STREAM << "SingleShotAO::write_initial(): entering... !" << endl;

	double l_val;
	cbd.tga->get_write_value(l_val);

	std::string l_attr_name = cbd.dya->get_name();
	yat::uint16 l_idx = extractNumber(l_attr_name, kINITIAL); // extract channel nb

	CHECK_MANAGER();
	try
	{
		m_manager->set_initial(l_idx, l_val);
	}
	catch(Tango::DevFailed& df)
	{
		ERROR_STREAM << df<< endl;
		RETHROW_DEVFAILED(df,
			"DRIVER_FAILURE",
			"could not write initial [caught Tango::DevFailed]",
			"SingleShotAO::write_initial");
	}
	catch(...)
	{
		ERROR_STREAM << "SingleShotAO::write_initial::unknown exception caught"<<std::endl;
		THROW_DEVFAILED("DRIVER_FAILURE",
			"could not write initial [unknown error]",
			"SingleShotAO::write_initial");
	}

	yat4tango::PropertyHelper::set_memorized_attribute(this, l_attr_name, l_val);
}


//+------------------------------------------------------------------
/**
 * set the write value of a dynamic attribute
 * used to keep read and write values in sync
 */
bool SingleShotAO::setDynamicAttributeWriteValue(const std::string& attrName, double value)
{
    try
	{
        Tango::DeviceImpl* dev = static_cast<Tango::DeviceImpl*>(this);
        Tango::DevDouble val_to_write = value;
        
        // Get the attribute
        Tango::WAttribute& attr = dev->get_device_attr()->get_w_attr_by_name(attrName.c_str());
        // Set the write value
        attr.set_write_value(&val_to_write, 1);
        DEBUG_STREAM << "Set write value for " << attrName << " to " << value << endl;
        return true;
    }
    catch (Tango::DevFailed &df)
	{
        ERROR_STREAM << "Failed to set write value for " << attrName 
                    << ": " << df << endl;
        return false;
    }
    catch (...)
	{
        ERROR_STREAM << "Unknown exception setting write value for " 
                    << attrName << endl;
        return false;
    }
}


//+------------------------------------------------------------------
/**
 *	method:	SingleShotAO::abort
 *
 *	description:	method to execute "Abort"
 *	Aborts ramps in progress.
 *
 */
//+------------------------------------------------------------------
void SingleShotAO::abort()
{
	DEBUG_STREAM << "SingleShotAO::abort(): entering... !" << endl;
	try
	{
		_abort();
	}
	catch(Tango::DevFailed& df)
	{
		ERROR_STREAM << df<< endl;
		RETHROW_DEVFAILED(df,
			"DRIVER_FAILURE",
			"could not abort [caught Tango::DevFailed]",
			"SingleShotAO::abort");
	}
	catch(...)
	{
		ERROR_STREAM << "SingleShotAO::abort::unknown exception caught"<< std::endl;
		THROW_DEVFAILED("DRIVER_FAILURE",
			"could not abort [unknown error]",
			"SingleShotAO::abort");
	}
}

void SingleShotAO::_abort()
{
	CHECK_MANAGER();

	// memorize the current initial_values and value of channels
	for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++)
	{
		if (m_manager->is_running(l_cpt))
		{
			DEBUG_STREAM << "Channel " << l_cpt << " is running. Memorizing values..." << std::endl;

			double l_val_channel = m_manager->get_channel(l_cpt);
			std::string l_channel_attr_name = kCHANNEL + std::to_string(l_cpt);
			yat4tango::PropertyHelper::set_memorized_attribute(this, l_channel_attr_name, l_val_channel);
			setDynamicAttributeWriteValue(l_channel_attr_name, l_val_channel);
			DEBUG_STREAM << "Memorizing " << l_channel_attr_name << " to " << l_val_channel << std::endl;

			std::string l_initial_attr_name = kINITIAL + std::to_string(l_cpt);
			yat4tango::PropertyHelper::set_memorized_attribute(this, l_initial_attr_name, l_val_channel);
			setDynamicAttributeWriteValue(l_initial_attr_name, l_val_channel);
			DEBUG_STREAM << "Memorizing " << l_initial_attr_name << " to " << l_val_channel << std::endl;
		}
	}
	m_manager->abort();
}

}	//	namespace