Skip to content
Snippets Groups Projects
Select Git revision
  • 9b56c0ccfd96170364548a8c85ae28e796f007c4
  • main default protected
  • release_2_1_0
  • release_2_0_4
  • release_2_0_2
  • release_2_0_1
  • release_2_0_0
  • release_1_0_4
  • release_1_0_3
  • release_1_0_2
  • release_1_0_1
  • v0
  • release_1_0_0
  • release_0_1
14 results

SingleShotAO.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    SingleShotAO.cpp 38.31 KiB
    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