From 3d756be15fa57fa37dc46c56a26138a47743e2ea Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Fri, 21 Feb 2025 16:35:50 +0100 Subject: [PATCH 01/13] fix: update format precision for channelX and speedX attributes to match initialX attributes --- src/SingleShotAO.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 699767f..ee7b789 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -429,7 +429,7 @@ void SingleShotAO::init_device() 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 = "%1.1f"; + dai_channel.tai.format = "%1.2f"; dai_channel.memorized = true; dai_channel.cdb = false; @@ -461,7 +461,7 @@ void SingleShotAO::init_device() 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 = "%1.1f"; + dai_speed.tai.format = "%1.2f"; //- cleanup tango db option: cleanup tango db when removing this dyn. attr. (i.e. erase its properties from db) dai_speed.memorized = true; -- GitLab From 915889e15d7f6e1d89442db101d42456127f3040 Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Fri, 21 Feb 2025 16:40:59 +0100 Subject: [PATCH 02/13] fix: rename get_device_properties back to get_device_property for consistency (+ add comments) --- src/SingleShotAO.cpp | 45 +++++++++++++++++++++----------------------- src/SingleShotAO.h | 2 +- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index ee7b789..96a72ba 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -247,7 +247,7 @@ void SingleShotAO::init_device() //-------------------------------------------- try { - get_device_properties(); + get_device_property(); } catch (const Tango::DevFailed& df) { @@ -570,57 +570,52 @@ void SingleShotAO::init_device() //+---------------------------------------------------------------------------- // -// method : SingleShotAO::get_device_properties() +// method : SingleShotAO::get_device_property() // // description : Read the device properties from database. // //----------------------------------------------------------------------------- -void SingleShotAO::get_device_properties() +void SingleShotAO::get_device_property() { - // Initialize your default values here (if not done with POGO). - //------------------------------------------------------------------ - - // Read device properties from database.(Automatic code generation) + // Read device properties from database //------------------------------------------------------------------ Tango::DbData dev_prop; dev_prop.push_back(Tango::DbDatum("BoardNum")); dev_prop.push_back(Tango::DbDatum("BoardType")); - // Call database and extract values + // Call database and extract values //-------------------------------------------- - if (Tango::Util::instance()->_UseDb==true) + 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())); + } + Tango::DbDatum def_prop, cl_prop; + SingleShotAOClass *ds_class = (static_cast<SingleShotAOClass *>(get_device_class())); int i = -1; - // Try to initialize BoardNum from class property + // 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 + // 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; } - // And try to extract BoardNum value from database + // And try to extract BoardNum value from database if (dev_prop[i].is_empty()==false) dev_prop[i] >> boardNum; - // Try to initialize BoardType from class property + // 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 + // 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; } - // And try to extract BoardType value from database + // And try to extract BoardType value from database if (dev_prop[i].is_empty()==false) dev_prop[i] >> boardType; - - - // End of Automatic code generation - //------------------------------------------------------------------ + // Check critical properties being present + //-------------------------------------------- critical_properties_missing = false; @@ -639,8 +634,11 @@ void SingleShotAO::get_device_properties() return; } + // Check critical properties being valid + //-------------------------------------------- + //- <BoardNum> ----------------------- - if (boardNum > 7) + if (boardNum < 0 || boardNum > 7) { boardNum = 0; ERROR_STREAM << "device property <BoardNum> is invalid. Valid range is [0..7]" << endl; @@ -665,7 +663,6 @@ void SingleShotAO::get_device_properties() ERROR_STREAM << "device property <BoardType> is invalid [supported hw: MAO_6208 or MAO_6216]" << endl; critical_properties_missing = true; } - } diff --git a/src/SingleShotAO.h b/src/SingleShotAO.h index 8e259d0..7faa182 100755 --- a/src/SingleShotAO.h +++ b/src/SingleShotAO.h @@ -219,7 +219,7 @@ public : /** * Read the device properties from database */ - void get_device_properties(); + void get_device_property(); //@} // Here is the end of the automatic code generation part -- GitLab From 1085d8888f3e1935e49ac54ab85630cf9c67c76f Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Fri, 21 Feb 2025 16:58:02 +0100 Subject: [PATCH 03/13] feat: add EnableRamps and WriteMemorizedValuesAtInit properties with default values (do nothing so far) --- doc/doc_html/Properties.html | 34 ++++++++- src/SingleShotAO.cpp | 31 ++++++++ src/SingleShotAO.h | 8 ++ src/SingleShotAOClass.cpp | 144 ++++++++++------------------------- 4 files changed, 109 insertions(+), 108 deletions(-) diff --git a/doc/doc_html/Properties.html b/doc/doc_html/Properties.html index 388a562..b0ca154 100755 --- a/doc/doc_html/Properties.html +++ b/doc/doc_html/Properties.html @@ -56,16 +56,34 @@ <Font Size=-1>Tango::DEV_STRING</Font> </Td> <Td> - <Font Size=-1>The board type [MAO_xxxx - where <xxxx> is the ADlink board identifier - e.g. MAO_6208 - no - default value]</Font> + <Font Size=-1>The board type [MAO_xxxx - where <xxxx> is the ADlink board identifier - e.g. MAO_6208 - no default value]</Font> </Td> </Tr> - </Table> + <Tr> + <Td><b><a href=#Dev_DefaultValues>EnableRamps </a></b></Td> + <Td> + <Font Size=-1>Tango::DEV_BOOLEAN</Font> + </Td> + <Td> + <Font Size=-1>Whether to enable or disable the ramp generation on the board output channels. If false, the speedX and initialX dynamic attributes will not be created for every channel, and the changes will happen instantly. [true/false - default value is true]</Font> + </Td> + + </Tr> + <Tr> + <Td><b><a href=#Dev_DefaultValues>WriteMemorizedValuesAtInit </a></b></Td> + <Td> + <Font Size=-1>Tango::DEV_BOOLEAN</Font> + </Td> + <Td> + <Font Size=-1>Whether to write the memorized values to the board at the device initialization. [true/false - default value is false]</Font> + </Td> + </Tr> + </Table> + </Center> - <A name=Dev_DefaultValues><!--- ---></a> <Font Size=+1>Device Properties Default Values:</Font><Br> <Table Border=2 Cellpadding=2 CELLSPACING=2> <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor"> @@ -80,6 +98,14 @@ <Td>BoardType</Td> <td>No default value</td> </Tr> + <Tr> + <Td>EnableRamps</Td> + <td>true</td> + </Tr> + <Tr> + <Td>WriteMemorizedValuesAtInit</Td> + <td>false</td> + </Tr> </Table> <Br><Br><Br> diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 96a72ba..56cb44d 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -582,6 +582,8 @@ void SingleShotAO::get_device_property() 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("WriteMemorizedValuesAtInit")); // Call database and extract values //-------------------------------------------- @@ -592,6 +594,7 @@ void SingleShotAO::get_device_property() 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; @@ -603,6 +606,7 @@ void SingleShotAO::get_device_property() // And try to extract BoardNum value from database if (dev_prop[i].is_empty()==false) dev_prop[i] >> boardNum; + //- <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; @@ -614,6 +618,33 @@ void SingleShotAO::get_device_property() // And try to extract BoardType value from database if (dev_prop[i].is_empty()==false) dev_prop[i] >> boardType; + //- <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; + } + // And try to extract EnableRamps value from database + if (dev_prop[i].is_empty() == false) dev_prop[i] >> enableRamps; + + //- <WriteMemorizedValuesAtInit> ----------------------- + // Try to initialize WriteMemorizedValuesAtInit from class property + cl_prop = ds_class->get_class_property(dev_prop[++i].name); + if (cl_prop.is_empty() == false) cl_prop >> writeMemorizedValuesAtInit; + else + { + // Try to initialize WriteMemorizedValuesAtInit from default device value + def_prop = ds_class->get_default_device_property(dev_prop[i].name); + if (def_prop.is_empty() == false) def_prop >> writeMemorizedValuesAtInit; + } + // And try to extract WriteMemorizedValuesAtInit value from database + if (dev_prop[i].is_empty() == false) dev_prop[i] >> writeMemorizedValuesAtInit; + + // Check critical properties being present //-------------------------------------------- diff --git a/src/SingleShotAO.h b/src/SingleShotAO.h index 7faa182..f77367e 100755 --- a/src/SingleShotAO.h +++ b/src/SingleShotAO.h @@ -110,6 +110,14 @@ public : * The board type [MAO_xxxx - where <xxxx> is the ADlink board identifier - e.g. MAO_6208 - no default value] */ string boardType; +/** + * Whether to enable or disable the ramp generation on the board output channels. If false, the speedX and initialX dynamic attributes will not be created for every channel, and the changes will happen instantly. [true/false - default value is true] + */ + Tango::DevBoolean enableRamps; +/** + * Whether to write the memorized values to the board at the device initialization. [true/false - default value is false] + */ + Tango::DevBoolean writeMemorizedValuesAtInit; //@} /** diff --git a/src/SingleShotAOClass.cpp b/src/SingleShotAOClass.cpp index e93187a..87816eb 100755 --- a/src/SingleShotAOClass.cpp +++ b/src/SingleShotAOClass.cpp @@ -369,10 +369,14 @@ void SingleShotAOClass::set_default_property() string prop_def; vector<string> vect_data; + // Set Default Class Properties + // ... + // Set Default Device Properties + //- <BoardNum> ----------------------- prop_name = "BoardNum"; - prop_desc = "The the board identifier in the cPCI crate [valid range is 0...7 - no default value] ."; + prop_desc = "The board identifier in the cPCI crate [valid range is 0...7 - no default value]."; prop_def = ""; vect_data.clear(); if (prop_def.length()>0) @@ -385,6 +389,7 @@ void SingleShotAOClass::set_default_property() else add_wiz_dev_prop(prop_name, prop_desc); + //- <BoardType> ----------------------- prop_name = "BoardType"; prop_desc = "The board type [MAO_xxxx - where <xxxx> is the ADlink board identifier - e.g. MAO_6208 - no default value]"; prop_def = ""; @@ -399,7 +404,40 @@ void SingleShotAOClass::set_default_property() else add_wiz_dev_prop(prop_name, prop_desc); + //- <EnableRamps> ----------------------- + prop_name = "EnableRamps"; + prop_desc = "Whether to enable or disable the ramp generation on the board output channels. If false, the speedX and initialX dynamic attributes will not be created for every channel, and the changes will happen instantly. [true/false - default value is true]"; + prop_def = "true"; + vect_data.clear(); + vect_data.push_back("true"); + if (prop_def.length() > 0) + { + Tango::DbDatum data(prop_name); + data << vect_data; + dev_def_prop.push_back(data); + add_wiz_dev_prop(prop_name, prop_desc, prop_def); + } + else + add_wiz_dev_prop(prop_name, prop_desc); + + //- <WriteMemorizedValuesAt> ----------------------- + prop_name = "WriteMemorizedValuesAtInit"; + prop_desc = "Whether to write the memorized values to the board at the device initialization. [true/false - default value is false]"; + prop_def = "false"; + vect_data.clear(); + vect_data.push_back("false"); + if (prop_def.length() > 0) + { + Tango::DbDatum data(prop_name); + data << vect_data; + dev_def_prop.push_back(data); + add_wiz_dev_prop(prop_name, prop_desc, prop_def); + } + else + add_wiz_dev_prop(prop_name, prop_desc); } + + //+---------------------------------------------------------------------------- // // method : SingleShotAOClass::write_class_property @@ -431,107 +469,6 @@ void SingleShotAOClass::write_class_property() str_desc.push_back("ADLink boards support for single shot AO operations [PCI-6208 and compatible boards]"); description << str_desc; data.push_back(description); - - // put cvs or svn location - string filename(classname); - filename += "Class.cpp"; - - // Create a string with the class ID to - // get the string into the binary - string class_id(ClassId); - - // check for cvs information - string src_path(CvsPath); - start = src_path.find("/"); - if (start!=string::npos) - { - end = src_path.find(filename); - if (end>start) - { - string strloc = src_path.substr(start, end-start); - // Check if specific repository - start = strloc.find("/cvsroot/"); - if (start!=string::npos && start>0) - { - string repository = strloc.substr(0, start); - if (repository.find("/segfs/")!=string::npos) - strloc = "ESRF:" + strloc.substr(start, strloc.length()-start); - } - Tango::DbDatum cvs_loc("cvs_location"); - cvs_loc << strloc; - data.push_back(cvs_loc); - } - } - // check for svn information - else - { - string src_path(SvnPath); - start = src_path.find("://"); - if (start!=string::npos) - { - end = src_path.find(filename); - if (end>start) - { - header = "$HeadURL: "; - start = header.length(); - string strloc = src_path.substr(start, (end-start)); - - Tango::DbDatum svn_loc("svn_location"); - svn_loc << strloc; - data.push_back(svn_loc); - } - } - } - - // Get CVS or SVN revision tag - - // CVS tag - string tagname(TagName); - header = "$Name: "; - start = header.length(); - string endstr(" $"); - - end = tagname.find(endstr); - if (end!=string::npos && end>start) - { - string strtag = tagname.substr(start, end-start); - Tango::DbDatum cvs_tag("cvs_tag"); - cvs_tag << strtag; - data.push_back(cvs_tag); - } - - // SVN tag - string svnpath(SvnPath); - header = "$HeadURL: "; - start = header.length(); - - end = svnpath.find(endstr); - if (end!=string::npos && end>start) - { - string strloc = svnpath.substr(start, end-start); - - string tagstr ("/tags/"); - start = strloc.find(tagstr); - if ( start!=string::npos ) - { - start = start + tagstr.length(); - end = strloc.find(filename); - string strtag = strloc.substr(start, end-start-1); - - Tango::DbDatum svn_tag("svn_tag"); - svn_tag << strtag; - data.push_back(svn_tag); - } - } - - // Get URL location - string httpServ(HttpServer); - if (httpServ.length()>0) - { - Tango::DbDatum db_doc_url("doc_url"); - db_doc_url << httpServ; - data.push_back(db_doc_url); - } // Put inheritance Tango::DbDatum inher_datum("InheritedFrom"); @@ -540,8 +477,7 @@ void SingleShotAOClass::write_class_property() inher_datum << inheritance; data.push_back(inher_datum); - // Call database and and values - //-------------------------------------------- + // Call database and add values get_db_class()->put_property(data); } -- GitLab From a1e6b1c7fff680f637fdbd2f3031cc36943c6520 Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Fri, 21 Feb 2025 17:01:31 +0100 Subject: [PATCH 04/13] feat: write memorized value at init if writeMemorizedValuesAtInit is true (instead of just set_channel) --- src/SingleShotAO.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 56cb44d..dd92d5a 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -537,8 +537,10 @@ void SingleShotAO::init_device() // Get and set memorized values for speed, initial and channel applyMemorizedAttr(kSPEED, &SingleShotAOManager::set_speed); applyMemorizedAttr(kINITIAL, &SingleShotAOManager::set_initial); - applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::set_channel); - // TODO: add a property to call write_channel instead of set_channel (false by default) + if (writeMemorizedValuesAtInit) + applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::write_channel); + else + applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::set_channel); } //- GO for task -- GitLab From 2fbc7f47a8820d8af04f917b601c49f85bc442ee Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Fri, 21 Feb 2025 17:26:14 +0100 Subject: [PATCH 05/13] feat: deactive ramp functionnality if enableRamps is false (don't create speed and initial attributes at init, and set changes instantly) --- src/SingleShotAO.cpp | 23 ++++++++++++++++------- src/SingleShotAOManager.cpp | 18 ++++++++++++++++-- src/SingleShotAOManager.h | 7 +++++-- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index dd92d5a..5bce000 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -361,7 +361,7 @@ void SingleShotAO::init_device() //-------------------------------------------- try { - m_manager->init(m_ssao, m_nb_chan, m_frequency); + m_manager->init(m_ssao, m_nb_chan, m_frequency, enableRamps); } catch (Tango::DevFailed & df) { @@ -443,6 +443,11 @@ void SingleShotAO::init_device() 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 ║ @@ -535,12 +540,16 @@ void SingleShotAO::init_device() }; // Get and set memorized values for speed, initial and channel - applyMemorizedAttr(kSPEED, &SingleShotAOManager::set_speed); - applyMemorizedAttr(kINITIAL, &SingleShotAOManager::set_initial); - if (writeMemorizedValuesAtInit) - applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::write_channel); - else - applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::set_channel); + if (enableRamps) { + applyMemorizedAttr(kSPEED, &SingleShotAOManager::set_speed); + applyMemorizedAttr(kINITIAL, &SingleShotAOManager::set_initial); + } + + if (writeMemorizedValuesAtInit) { + applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::write_channel); // write memorized value to board output + } else { + applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::set_channel); // only apply memorized value to the device + } } //- GO for task diff --git a/src/SingleShotAOManager.cpp b/src/SingleShotAOManager.cpp index e0519ec..0eb1333 100755 --- a/src/SingleShotAOManager.cpp +++ b/src/SingleShotAOManager.cpp @@ -128,7 +128,7 @@ void SingleShotAOManager::write_frequency(double p_frequency) // ============================================================================ // SingleShotAOManager::init () // ============================================================================ -void SingleShotAOManager::init(asl::SingleShotAO * p_ssao, unsigned short p_nb_chan, double p_frequency) +void SingleShotAOManager::init(asl::SingleShotAO * p_ssao, unsigned short p_nb_chan, double p_frequency, bool p_enable_ramps) { m_ssao = p_ssao; CHECK_SSAO(); @@ -145,6 +145,8 @@ void SingleShotAOManager::init(asl::SingleShotAO * p_ssao, unsigned short p_nb_c enable_periodic_msg(true); } + m_enable_ramps = p_enable_ramps; + // initialize channel indexes (-1 means no ramp in progress) // and ramp states for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++) @@ -288,7 +290,7 @@ void SingleShotAOManager::write_channel(ChannelId_t p_chIdx, double p_val) DEBUG_STREAM << "write_channel " << p_chIdx << " : " << p_val << endl; // if the speed is 0, write the value directly and skip ramp - if (m_speeds[p_chIdx] == 0.0) + if (m_speeds[p_chIdx] == 0.0 || !m_enable_ramps) { try { @@ -409,6 +411,12 @@ void SingleShotAOManager::set_initial(ChannelId_t p_chIdx, Intial_t p_initial) "could not write initial : a ramp is still in progress on this channel", "SingleShotAOManager::set_initial"); } + else if (!m_enable_ramps) + { + THROW_DEVFAILED("DEVICE_FAILURE", + "could not write initial : ramps are disabled", + "SingleShotAOManager::set_initial"); + } else { m_initials[p_chIdx] = p_initial; @@ -434,6 +442,12 @@ void SingleShotAOManager::set_speed(ChannelId_t p_chIdx, Intial_t p_speed) "could not write speed : a ramp is still in progress on this channel", "SingleShotAOManager::set_speed"); } + else if (!m_enable_ramps) + { + THROW_DEVFAILED("DEVICE_FAILURE", + "could not write speed : ramps are disabled", + "SingleShotAOManager::set_speed"); + } else { m_speeds[p_chIdx] = p_speed; diff --git a/src/SingleShotAOManager.h b/src/SingleShotAOManager.h index a021036..5351b59 100755 --- a/src/SingleShotAOManager.h +++ b/src/SingleShotAOManager.h @@ -48,7 +48,7 @@ public: std::string get_status (); //- init - void init(asl::SingleShotAO * p_ssao, unsigned short p_nb_chan, double p_frequency); + void init(asl::SingleShotAO *p_ssao, unsigned short p_nb_chan, double p_frequency, bool p_enable_ramps); //- get current channel value double get_channel(ChannelId_t p_chIdx); @@ -100,7 +100,10 @@ private: //- frequency double m_frequency; - + + //-enable ramps + bool m_enable_ramps; + //- initial buffer for all channels std::map<ChannelId_t, Intial_t> m_initials; -- GitLab From 9547d4baa7101eac7b0ea23562f1be24fe3ff15c Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Fri, 21 Feb 2025 17:27:51 +0100 Subject: [PATCH 06/13] bump: update version to 2.2.0 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 24ed606..27e4801 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,7 +3,7 @@ from conan import ConanFile class SingleShotAORecipe(ConanFile): name = "singleshotao" executable = "ds_SingleShotAO" - version = "2.1.0" + version = "2.2.0" package_type = "application" user = "soleil" python_requires = "base/[>=1.0]@soleil/stable" -- GitLab From acaf27f501d07516df4cb72b913951affc84d7e3 Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Fri, 21 Feb 2025 17:35:14 +0100 Subject: [PATCH 07/13] feat: add logging for parsed device properties in get_device_property method --- src/SingleShotAO.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 5bce000..b5ed3f3 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -601,6 +601,7 @@ void SingleShotAO::get_device_property() 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; @@ -616,6 +617,7 @@ void SingleShotAO::get_device_property() } // 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 @@ -628,39 +630,37 @@ void SingleShotAO::get_device_property() } // 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 - { + 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; } // 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; //- <WriteMemorizedValuesAtInit> ----------------------- // Try to initialize WriteMemorizedValuesAtInit from class property cl_prop = ds_class->get_class_property(dev_prop[++i].name); if (cl_prop.is_empty() == false) cl_prop >> writeMemorizedValuesAtInit; - else - { + else { // Try to initialize WriteMemorizedValuesAtInit from default device value def_prop = ds_class->get_default_device_property(dev_prop[i].name); if (def_prop.is_empty() == false) def_prop >> writeMemorizedValuesAtInit; } // And try to extract WriteMemorizedValuesAtInit value from database if (dev_prop[i].is_empty() == false) dev_prop[i] >> writeMemorizedValuesAtInit; - + INFO_STREAM << "WriteMemorizedValuesAtInit parsed: " << (writeMemorizedValuesAtInit ? "true" : "false") << endl; // Check critical properties being present //-------------------------------------------- - critical_properties_missing = false; - if (dev_prop[0].is_empty()) { ERROR_STREAM << "Required device property <BoardNum> is missing" << endl; @@ -671,20 +671,20 @@ void SingleShotAO::get_device_property() ERROR_STREAM << "Required device property <BoardType> is missing" << endl; critical_properties_missing = true; } - if (critical_properties_missing) { return; } // Check critical properties being valid //-------------------------------------------- - //- <BoardNum> ----------------------- if (boardNum < 0 || boardNum > 7) { boardNum = 0; ERROR_STREAM << "device property <BoardNum> is invalid. Valid range is [0..7]" << endl; critical_properties_missing = true; + } else { + INFO_STREAM << "BoardNum resolved to " << boardNum << endl; } //- <BoardType> ----------------------- @@ -692,11 +692,13 @@ void SingleShotAO::get_device_property() { 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; } else { -- GitLab From 64fc34e7f901f62dd54aa092b5354ffdbd13aa66 Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Mon, 17 Mar 2025 18:20:47 +0100 Subject: [PATCH 08/13] feat: rename WriteMemorizedValuesAtInit to OutputMemorizedChannelsAtInit and update documentation --- doc/doc_html/Properties.html | 6 +++--- src/SingleShotAO.cpp | 20 ++++++++++---------- src/SingleShotAO.h | 2 +- src/SingleShotAOClass.cpp | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/doc/doc_html/Properties.html b/doc/doc_html/Properties.html index b0ca154..95f05a3 100755 --- a/doc/doc_html/Properties.html +++ b/doc/doc_html/Properties.html @@ -72,12 +72,12 @@ </Tr> <Tr> - <Td><b><a href=#Dev_DefaultValues>WriteMemorizedValuesAtInit </a></b></Td> + <Td><b><a href=#Dev_DefaultValues>OutputMemorizedChannelsAtInit </a></b></Td> <Td> <Font Size=-1>Tango::DEV_BOOLEAN</Font> </Td> <Td> - <Font Size=-1>Whether to write the memorized values to the board at the device initialization. [true/false - default value is false]</Font> + <Font Size=-1>Whether to send the memorized values for the channels to the board at the device initialization (the device attribute will show the last memorized value regardless) [true/false - default value is false]</Font> </Td> </Tr> </Table> @@ -103,7 +103,7 @@ <td>true</td> </Tr> <Tr> - <Td>WriteMemorizedValuesAtInit</Td> + <Td>OutputMemorizedChannelsAtInit</Td> <td>false</td> </Tr> </Table> diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index b5ed3f3..748d7f1 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -545,7 +545,7 @@ void SingleShotAO::init_device() applyMemorizedAttr(kINITIAL, &SingleShotAOManager::set_initial); } - if (writeMemorizedValuesAtInit) { + if (outputMemorizedChannelsAtInit) { applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::write_channel); // write memorized value to board output } else { applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::set_channel); // only apply memorized value to the device @@ -594,7 +594,7 @@ void SingleShotAO::get_device_property() 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("WriteMemorizedValuesAtInit")); + dev_prop.push_back(Tango::DbDatum("OutputMemorizedChannelsAtInit")); // Call database and extract values //-------------------------------------------- @@ -645,18 +645,18 @@ void SingleShotAO::get_device_property() if (dev_prop[i].is_empty() == false) dev_prop[i] >> enableRamps; INFO_STREAM << "EnableRamps parsed: " << (enableRamps ? "true" : "false") << endl; - //- <WriteMemorizedValuesAtInit> ----------------------- - // Try to initialize WriteMemorizedValuesAtInit from class property + //- <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 >> writeMemorizedValuesAtInit; + if (cl_prop.is_empty() == false) cl_prop >> outputMemorizedChannelsAtInit; else { - // Try to initialize WriteMemorizedValuesAtInit from default device value + // 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 >> writeMemorizedValuesAtInit; + if (def_prop.is_empty() == false) def_prop >> outputMemorizedChannelsAtInit; } - // And try to extract WriteMemorizedValuesAtInit value from database - if (dev_prop[i].is_empty() == false) dev_prop[i] >> writeMemorizedValuesAtInit; - INFO_STREAM << "WriteMemorizedValuesAtInit parsed: " << (writeMemorizedValuesAtInit ? "true" : "false") << endl; + // 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; // Check critical properties being present //-------------------------------------------- diff --git a/src/SingleShotAO.h b/src/SingleShotAO.h index f77367e..7cee441 100755 --- a/src/SingleShotAO.h +++ b/src/SingleShotAO.h @@ -117,7 +117,7 @@ public : /** * Whether to write the memorized values to the board at the device initialization. [true/false - default value is false] */ - Tango::DevBoolean writeMemorizedValuesAtInit; + Tango::DevBoolean outputMemorizedChannelsAtInit; //@} /** diff --git a/src/SingleShotAOClass.cpp b/src/SingleShotAOClass.cpp index 87816eb..29a1ef5 100755 --- a/src/SingleShotAOClass.cpp +++ b/src/SingleShotAOClass.cpp @@ -420,9 +420,9 @@ void SingleShotAOClass::set_default_property() else add_wiz_dev_prop(prop_name, prop_desc); - //- <WriteMemorizedValuesAt> ----------------------- - prop_name = "WriteMemorizedValuesAtInit"; - prop_desc = "Whether to write the memorized values to the board at the device initialization. [true/false - default value is false]"; + //- <OutputMemorizedChannelsAtInit> ----------------------- + prop_name = "OutputMemorizedChannelsAtInit"; + prop_desc = "Whether to send the memorized values for the channels to the board at the device initialization (the device attribute will show the last memorized value regardless) [true/false - default value is false]"; prop_def = "false"; vect_data.clear(); vect_data.push_back("false"); -- GitLab From 2c69e3b7c591c5cc9563e950096954a8b0a35a6d Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Mon, 17 Mar 2025 18:21:51 +0100 Subject: [PATCH 09/13] feat: add ASL dependency in info attribute (hardcoded version because I could not find a variable for the version) --- src/SingleShotAO.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 748d7f1..3257a7b 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -216,8 +216,7 @@ void SingleShotAO::init_device() 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, "Dependency Name", YAT_XSTR(dependency_name_PROJECT_VERSION)); - // TODO: Add dependencies + yat4tango::DeviceInfo::add_dependency(this, "ASL", ">=1.0"); } catch (Tango::DevFailed &df) { -- GitLab From 646f4650d6e3e4a4818b6be371dec2521b8cbfe5 Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Mon, 17 Mar 2025 18:22:45 +0100 Subject: [PATCH 10/13] feat: added logging and consolidate dynamic attribute management --- src/SingleShotAO.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 3257a7b..cb204a0 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -318,6 +318,7 @@ void SingleShotAO::init_device() m_nb_chan = 16; } + INFO_STREAM << "Board has " << m_nb_chan << " channels" << endl; // construct the AO manager //-------------------------------------------- @@ -377,6 +378,20 @@ void SingleShotAO::init_device() 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 //-------------------------------------------- @@ -404,6 +419,17 @@ void SingleShotAO::init_device() // 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; + + // if enableRamps if false, skip speed and initial attributes + if (!enableRamps) + { + INFO_STREAM << "Ramps are disabled. Skipping speed and initial attributes" << std::endl; + } + for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++) { yat::OSStream oss; @@ -515,8 +541,45 @@ void SingleShotAO::init_device() l_dynAttrList.push_back(dai_initial); } - m_dyn_attr_manager->add_attributes(l_dynAttrList); + + // 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++) @@ -531,6 +594,7 @@ void SingleShotAO::init_device() try { std::string attrName = attrPrefix + oss.str(); double val = yat4tango::PropertyHelper::get_memorized_attribute<double>(this, attrName); + DEBUG_STREAM << "Found memorized value for " << attrName << ": " << val << endl; (m_manager->*setter)(l_cpt, val); } catch (...) { -- GitLab From 5551cf17caf5eefd2a2e6aa40bbe3f2b8d645dde Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Mon, 24 Mar 2025 17:50:41 +0100 Subject: [PATCH 11/13] refactor: split write_channel method into write_channel_direct and start_channel_ramp --- src/SingleShotAO.cpp | 5 +- src/SingleShotAOManager.cpp | 121 ++++++++++++++++++++---------------- src/SingleShotAOManager.h | 6 ++ 3 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index cb204a0..7bcea21 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -609,7 +609,7 @@ void SingleShotAO::init_device() } if (outputMemorizedChannelsAtInit) { - applyMemorizedAttr(kCHANNEL, &SingleShotAOManager::write_channel); // write memorized value to board output + 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 } @@ -978,8 +978,11 @@ void SingleShotAO::write_channel(yat4tango::DynamicAttributeWriteCallbackData & "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); + yat4tango::PropertyHelper::set_memorized_attribute(this, kINITIAL + std::to_string(l_idx), l_val); + DEBUG_STREAM << "SingleShotAO::write_channel(): memorized attribute " << l_attr_name << " set to " << l_val << endl; } diff --git a/src/SingleShotAOManager.cpp b/src/SingleShotAOManager.cpp index 0eb1333..e988c22 100755 --- a/src/SingleShotAOManager.cpp +++ b/src/SingleShotAOManager.cpp @@ -250,6 +250,7 @@ void SingleShotAOManager::periodic_job_i() m_currentIndex[l_cpt] += 1; if (m_currentIndex[l_cpt] == m_ramps[l_cpt].capacity()) { + DEBUG_STREAM << "Ramp finished for channel" << l_cpt << endl; m_currentIndex[l_cpt] = -1; m_initials[l_cpt] = m_channels[l_cpt]; m_ramps[l_cpt].clear(); @@ -283,67 +284,43 @@ void SingleShotAOManager::set_channel(ChannelId_t p_chIdx, double p_val) } // ============================================================================ -// SingleShotAOManager::write_channel () +// SingleShotAOManager::write_channel_direct () // ============================================================================ -void SingleShotAOManager::write_channel(ChannelId_t p_chIdx, double p_val) +void SingleShotAOManager::write_channel_direct(ChannelId_t p_chIdx, double p_val) { - DEBUG_STREAM << "write_channel " << p_chIdx << " : " << p_val << endl; - - // if the speed is 0, write the value directly and skip ramp - if (m_speeds[p_chIdx] == 0.0 || !m_enable_ramps) + try { - try - { - CHECK_SSAO(); - m_ssao->write_scaled_channel((adl::ChanId)p_chIdx, p_val); - m_channels[p_chIdx] = p_val; - m_initials[p_chIdx] = p_val; - DEBUG_STREAM << "Speed is 0, writing directly the value" << std::endl; - } - catch (const asl::DAQException &de) - { - Tango::DevFailed df = daq_to_tango_exception(de); - ERROR_STREAM << df << std::endl; - m_state = Tango::FAULT; - RETHROW_DEVFAILED(df, - "DRIVER_FAILURE", - "could not write channel [caught asl::DAQException]", - "SingleShotAOManager::write_channel"); - } - catch (...) - { - ERROR_STREAM << "SingleShotAOManager::write_channel::unknown exception caught" << std::endl; - m_state = Tango::FAULT; - THROW_DEVFAILED("DRIVER_FAILURE", - "could not write channel [unknown error]", - "SingleShotAOManager::write_channel"); - } - return; + CHECK_SSAO(); + m_ssao->write_scaled_channel((adl::ChanId)p_chIdx, p_val); + m_channels[p_chIdx] = p_val; + m_initials[p_chIdx] = p_val; + DEBUG_STREAM << "Writing directly the value" << std::endl; } - - // if a ramp is running, error - if (m_isRunning[p_chIdx]) + catch (const asl::DAQException &de) { - THROW_DEVFAILED("DEVICE_FAILURE", - "could not write channel : a ramp is still in progress on this channel", - "SingleShotAOManager::write_channel"); + Tango::DevFailed df = daq_to_tango_exception(de); + ERROR_STREAM << df << std::endl; + m_state = Tango::FAULT; + RETHROW_DEVFAILED(df, + "DRIVER_FAILURE", + "could not write channel [caught asl::DAQException]", + "SingleShotAOManager::write_channel_direct"); } - - // if frequency = 0, error - if (m_frequency == 0) + catch (...) { + ERROR_STREAM << "SingleShotAOManager::write_channel_direct::unknown exception caught" << std::endl; + m_state = Tango::FAULT; THROW_DEVFAILED("DRIVER_FAILURE", - "could not set a ramp on this channel. The frequency is 0", - "SingleShotAOManager::write_channel"); - } - - // if initial = channel, skip - if (m_initials[p_chIdx] == p_val) - { - DEBUG_STREAM << "Initial value is the same as the given value, skipping" << endl; - return; + "could not write channel [unknown error]", + "SingleShotAOManager::write_channel_direct"); } +} +// ============================================================================ +// SingleShotAOManager::start_channel_ramp () +// ============================================================================ +void SingleShotAOManager::start_channel_ramp(ChannelId_t p_chIdx, double p_val) +{ // ramp determination double l_delta = p_val - m_initials[p_chIdx]; bool isDown = false; @@ -389,7 +366,47 @@ void SingleShotAOManager::write_channel(ChannelId_t p_chIdx, double p_val) m_ramps[p_chIdx].force_length(0); m_currentIndex[p_chIdx] = 0; m_ramps[p_chIdx] = l_buffer; - //m_channels[p_chIdx] = m_ramps[p_chIdx][0]; -- soso on ne met rien ici => à l'application +} + +// ============================================================================ +// SingleShotAOManager::write_channel () +// ============================================================================ +void SingleShotAOManager::write_channel(ChannelId_t p_chIdx, double p_val) +{ + DEBUG_STREAM << "write_channel " << p_chIdx << " : " << p_val << endl; + + // if the speed is 0, write the value directly and skip ramp + if (m_speeds[p_chIdx] == 0.0 || !m_enable_ramps) + { + write_channel_direct(p_chIdx, p_val); + return; + } + + // if a ramp is running, error + if (m_isRunning[p_chIdx]) + { + THROW_DEVFAILED("DEVICE_FAILURE", + "could not write channel : a ramp is still in progress on this channel", + "SingleShotAOManager::write_channel"); + } + + // if frequency = 0, error + if (m_frequency == 0) + { + THROW_DEVFAILED("DRIVER_FAILURE", + "could not set a ramp on this channel. The frequency is 0", + "SingleShotAOManager::write_channel"); + } + + // if initial = channel, skip + if (m_initials[p_chIdx] == p_val) + { + DEBUG_STREAM << "Initial value is the same as the given value, skipping" << endl; + return; + } + + // Create and start a ramp + start_channel_ramp(p_chIdx, p_val); } // ============================================================================ diff --git a/src/SingleShotAOManager.h b/src/SingleShotAOManager.h index 5351b59..25c002e 100755 --- a/src/SingleShotAOManager.h +++ b/src/SingleShotAOManager.h @@ -59,6 +59,12 @@ public: //- write channel void write_channel(ChannelId_t p_chIdx, double p_val); + //- write a channel directly (without ramp) + void write_channel_direct(ChannelId_t p_chIdx, double p_val); + + //- create and start a ramp for channel + void start_channel_ramp(ChannelId_t p_chIdx, double p_val); + //- change period void write_frequency(double p_frequency); -- GitLab From e3d05be346d74b5a50b53dae0f9be0eb234fbf70 Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Tue, 25 Mar 2025 10:26:35 +0100 Subject: [PATCH 12/13] fix: always synchronize read and write values (except during ramps) --- src/SingleShotAO.cpp | 61 +++++++++++++++++++++++++++++++++++++------- src/SingleShotAO.h | 4 +++ 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 7bcea21..76ce807 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -455,7 +455,6 @@ void SingleShotAO::init_device() dai_channel.tai.min_value = "-10.0"; dai_channel.tai.description = "Output value for channel " + oss.str() + " (in measurementUnit)."; dai_channel.tai.format = "%1.2f"; - dai_channel.memorized = true; dai_channel.cdb = false; //- read callback @@ -494,7 +493,6 @@ void SingleShotAO::init_device() dai_speed.tai.format = "%1.2f"; //- cleanup tango db option: cleanup tango db when removing this dyn. attr. (i.e. erase its properties from db) - dai_speed.memorized = true; dai_speed.cdb = false; //- read callback @@ -528,7 +526,6 @@ void SingleShotAO::init_device() dai_initial.tai.format = "%1.2f"; //- cleanup tango db option: cleanup tango db when removing this dyn. attr. (i.e. erase its properties from db) - dai_initial.memorized = true; dai_initial.cdb = false; //- read callback @@ -595,7 +592,12 @@ void SingleShotAO::init_device() std::string attrName = attrPrefix + oss.str(); 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 (...) { // nothing to do @@ -954,6 +956,7 @@ void SingleShotAO::write_channel(yat4tango::DynamicAttributeWriteCallbackData & { 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 @@ -981,7 +984,12 @@ void SingleShotAO::write_channel(yat4tango::DynamicAttributeWriteCallbackData & 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); - yat4tango::PropertyHelper::set_memorized_attribute(this, kINITIAL + std::to_string(l_idx), 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; } @@ -1116,6 +1124,37 @@ void SingleShotAO::write_initial(yat4tango::DynamicAttributeWriteCallbackData & } +//+------------------------------------------------------------------ +/** + * 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 @@ -1160,11 +1199,15 @@ void SingleShotAO::_abort() DEBUG_STREAM << "Channel " << l_cpt << " is running. Memorizing values..." << std::endl; double l_val_channel = m_manager->get_channel(l_cpt); - yat4tango::PropertyHelper::set_memorized_attribute(this, kCHANNEL + std::to_string(l_cpt), l_val_channel); - DEBUG_STREAM << "Memorizing channel " << l_cpt << " to " << l_val_channel << std::endl; - - yat4tango::PropertyHelper::set_memorized_attribute(this, kINITIAL + std::to_string(l_cpt), l_val_channel); - DEBUG_STREAM << "Memorizing initial " << l_cpt << " to " << l_val_channel << std::endl; + 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(); diff --git a/src/SingleShotAO.h b/src/SingleShotAO.h index 7cee441..17e2b74 100755 --- a/src/SingleShotAO.h +++ b/src/SingleShotAO.h @@ -290,6 +290,10 @@ protected : void _abort(); + // set the write value of a dynamic attribute + // used to keep read and write values in sync + bool setDynamicAttributeWriteValue(const std::string& attrName, double value); + }; } // namespace_ns -- GitLab From d77c4643378126b3d588ba492a619170dae27130 Mon Sep 17 00:00:00 2001 From: MALFREYT <alexandre.malfreyt@synchrotron-soleil.fr> Date: Wed, 26 Mar 2025 11:30:58 +0100 Subject: [PATCH 13/13] fix: applied requested changes (by @langlois in !3) --- src/SingleShotAO.cpp | 75 +++++++++++++++++++++++---------------- src/SingleShotAOClass.cpp | 3 +- src/SingleShotAOManager.h | 2 ++ 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/SingleShotAO.cpp b/src/SingleShotAO.cpp index 76ce807..0c95671 100755 --- a/src/SingleShotAO.cpp +++ b/src/SingleShotAO.cpp @@ -380,14 +380,17 @@ void SingleShotAO::init_device() // Remove existing dynamic attributes to avoid duplicates or other issues // -------------------------------------------- - if (m_dyn_attr_manager) { - try { + 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 (...) { + catch (...) + { ERROR_STREAM << "Error cleaning up existing dynamic attributes manager, continuing..." << endl; // Continue anyway - we'll create a new one } @@ -424,12 +427,6 @@ void SingleShotAO::init_device() INFO_STREAM << "Creating dynamic attributes for " << m_nb_chan << " channels."; INFO_STREAM << "Total attributes: " << m_nb_chan * (enableRamps ? 3 : 1) << endl; - // if enableRamps if false, skip speed and initial attributes - if (!enableRamps) - { - INFO_STREAM << "Ramps are disabled. Skipping speed and initial attributes" << std::endl; - } - for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++) { yat::OSStream oss; @@ -454,7 +451,7 @@ void SingleShotAO::init_device() 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 = "%1.2f"; + dai_channel.tai.format = "%2.1f"; dai_channel.cdb = false; //- read callback @@ -468,7 +465,8 @@ void SingleShotAO::init_device() l_dynAttrList.push_back(dai_channel); // if enableRamps if false, skip speed and initial attributes - if (!enableRamps) { + if (!enableRamps) + { DEBUG_STREAM << "Ramps are disabled. Skipping speed and initial attributes for channel " << l_cpt << std::endl; continue; } @@ -490,7 +488,7 @@ void SingleShotAO::init_device() 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 = "%1.2f"; + dai_speed.tai.format = "%2.1f"; //- cleanup tango db option: cleanup tango db when removing this dyn. attr. (i.e. erase its properties from db) dai_speed.cdb = false; @@ -523,7 +521,7 @@ void SingleShotAO::init_device() 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 = "%1.2f"; + dai_initial.tai.format = "%2.1f"; //- cleanup tango db option: cleanup tango db when removing this dyn. attr. (i.e. erase its properties from db) dai_initial.cdb = false; @@ -543,17 +541,20 @@ void SingleShotAO::init_device() INFO_STREAM << "Prepared " << l_dynAttrList.size() << " dynamic attributes for creation" << endl; // Add all attributes - try { + try + { m_dyn_attr_manager->add_attributes(l_dynAttrList); INFO_STREAM << "Successfully added all dynamic attributes" << endl; } - catch (Tango::DevFailed &df) { + 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 (...) { + catch (...) + { ERROR_STREAM << "Unknown exception when adding dynamic attributes" << endl; m_currStatus = "Failed to add dynamic attributes. Unknown error"; m_state = Tango::FAULT; @@ -561,17 +562,21 @@ void SingleShotAO::init_device() } // Initialize maps in manager class for all channels - try { - for (unsigned int l_cpt = 0; l_cpt < m_nb_chan; l_cpt++) { + 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) { + if (enableRamps) + { m_manager->set_speed(l_cpt, 0.0); m_manager->set_initial(l_cpt, 0.0); } } } - catch (Tango::DevFailed &df) { + 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; @@ -588,7 +593,8 @@ void SingleShotAO::init_device() auto applyMemorizedAttr = [&](const std::string& attrPrefix, void (SingleShotAOManager::*setter)(yat::uint16, double)) { - try { + try + { std::string attrName = attrPrefix + oss.str(); double val = yat4tango::PropertyHelper::get_memorized_attribute<double>(this, attrName); DEBUG_STREAM << "Found memorized value for " << attrName << ": " << val << endl; @@ -599,18 +605,21 @@ void SingleShotAO::init_device() // Write the value to the "write" attributes using helper function setDynamicAttributeWriteValue(attrName, val); } - catch (...) { + catch (...) + { // nothing to do } }; // Get and set memorized values for speed, initial and channel - if (enableRamps) { + if (enableRamps) + { applyMemorizedAttr(kSPEED, &SingleShotAOManager::set_speed); applyMemorizedAttr(kINITIAL, &SingleShotAOManager::set_initial); } - if (outputMemorizedChannelsAtInit) { + 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 @@ -663,7 +672,8 @@ void SingleShotAO::get_device_property() // Call database and extract values //-------------------------------------------- - if (Tango::Util::instance()->_UseDb==true) { + if (Tango::Util::instance()->_UseDb==true) + { get_db_device()->get_property(dev_prop); } @@ -736,7 +746,8 @@ void SingleShotAO::get_device_property() ERROR_STREAM << "Required device property <BoardType> is missing" << endl; critical_properties_missing = true; } - if (critical_properties_missing) { + if (critical_properties_missing) + { return; } @@ -1131,7 +1142,8 @@ void SingleShotAO::write_initial(yat4tango::DynamicAttributeWriteCallbackData & */ bool SingleShotAO::setDynamicAttributeWriteValue(const std::string& attrName, double value) { - try { + try + { Tango::DeviceImpl* dev = static_cast<Tango::DeviceImpl*>(this); Tango::DevDouble val_to_write = value; @@ -1142,12 +1154,14 @@ bool SingleShotAO::setDynamicAttributeWriteValue(const std::string& attrName, do DEBUG_STREAM << "Set write value for " << attrName << " to " << value << endl; return true; } - catch (Tango::DevFailed &df) { + catch (Tango::DevFailed &df) + { ERROR_STREAM << "Failed to set write value for " << attrName << ": " << df << endl; return false; } - catch (...) { + catch (...) + { ERROR_STREAM << "Unknown exception setting write value for " << attrName << endl; return false; @@ -1195,7 +1209,8 @@ void SingleShotAO::_abort() // 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)) { + 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); diff --git a/src/SingleShotAOClass.cpp b/src/SingleShotAOClass.cpp index 29a1ef5..d5b5ee1 100755 --- a/src/SingleShotAOClass.cpp +++ b/src/SingleShotAOClass.cpp @@ -87,7 +87,8 @@ __declspec(dllexport) #endif - Tango::DeviceClass *_create_SingleShotAO_class(const char *name) { + Tango::DeviceClass *_create_SingleShotAO_class(const char *name) + { return SingleShotAO_ns::SingleShotAOClass::init(name); } } diff --git a/src/SingleShotAOManager.h b/src/SingleShotAOManager.h index 25c002e..325e00f 100755 --- a/src/SingleShotAOManager.h +++ b/src/SingleShotAOManager.h @@ -54,9 +54,11 @@ public: double get_channel(ChannelId_t p_chIdx); //- set channel + // Updates the value for the channel in the device without sending it to hardware void set_channel(ChannelId_t p_chIdx, double p_val); //- write channel + // Writes value to the channel to the device and the hardware, with a ramp if speed is not null and EnableRamps property is true void write_channel(ChannelId_t p_chIdx, double p_val); //- write a channel directly (without ramp) -- GitLab