Since last public TDAQ release there are important public API changes, which can require modifications of the user's code!

Public API Changes: Migration to ERS

The interfaces of configuration packages were implemented long time ago, when used compilers did not supported exceptions. By that reason most of the methods of configuration classes returned boolean status to indicate that a method was successful or had a problem.

During summer 2006 the DAQ/HLT-I coordination group took a decision the ERS has to be used by all packages of TDAQ release to report problems starting from TDAQ release 01-07-00. Now the status reporting mechanism of methods of config classes has been changed: to report a problem they throw ERS exception and the return type of them is changed to void.

Each method of config classes and classes of data access libraries generated by genconfig has explicit exception specification. For complete information about particular method see generated Doxygen documentation or appropriate header files. The following config exceptions can be thrown:

All above exceptions have common class daq::config::Exception, which in turn is derived from the ers::Issue. The catch of daq::config::Exception is recommending to be used, if exact reason of error is not important for the user's code.

If the method's exception specification is throw(), then such method does not throw any exception.

Below there are several examples of changes.

Example 1. Loading of database

In the past to check status of the database loading it was necessary to use special method loaded() as shown below:

std::string data("oksconfig:db.data.xml");
Configuration db(data);
if(!db.loaded()) {
  std::cerr << "ERROR: cannot load database " << data << std::endl;
  return;
}
... // code working with database

Now the call of the method loaded() is not needed any more. One can catch exception in a common try / catch block as shown below:

try {
  Configuration db("oksconfig:db.data.xml");
  ... // code working with database
}
catch (daq::config::Exception & ex) {
  std::cerr << "Caught exception " << ex << std::endl;
  return;
}
Example 2. Get object by id

There are two ways to get an object by id: using config layer defined by this package and using generated data access library layer (see genconfig and e.g. dal packages).

The correct old code to get an object using the config layer was:

  // return true if object has been printed
bool print_obj(::Configuration& db, const std::string& class_name, const std::string& object_id) {
  ConfigObject obj;
    // try to get object
    // in case of an error, the plug-in reports the problem to standard error stream
  if(db.get(class_name, object_id, obj) == false) {
    std::cerr << "ERROR: cannot get object " << object_id << " of class " << class_name << std::endl;
    return false;
  }
  std::cout << obj;
  return true;
} 

Now it has to be replaced by:

  // return true if object has been printed
bool print_obj(::Configuration& db, const std::string& class_name, const std::string& object_id) throw() {
  try {
    ConfigObject obj;
    db.get(class_name, object_id, obj);
    std::cout << obj;
    return true;
  }
  catch (daq::config::NotFound& ex) {
    std::cerr << "ERROR: cannot get object " << object_id << " of class " << class_name << std::endl;
  }
  catch (daq::config::Generic& ex) { // catch and report plug-in errors
    std::cerr << "Caught exception " << ex << std::endl;
  }
  return false;
}

In case when generated DAL layer is used to get a template object by ID, the daq::config::NotFound exception is not thrown, but it is necessary to catch generic daq::config::Exception as shown below:

  // return true if object has been printed
bool print_application(::Configuration& db, const std::string& object_id) throw() {
  try {
    const daq::core::BaseApplication * a = db.get<daq::core::BaseApplication>(object_id);
    if(a) { std::cout << *a << std::endl; return true; }
    else  { std::cerr << "ERROR: there is no application object with id " << object_id << std::endl; }
  }
  catch (daq::config::Exception& ex) { // catch and report plug-in errors, also can use here daq::config::Generic
    std::cerr << "Caught exception " << ex << std::endl;
  }
  return false;
}
Example 3. Access objects of class by query

In a similar way to above, the config layer can throw daq:config::NotFound exception, if there is no class with given name, see:

const char * class_name = "Segment";
try {
  Configuration db("rdbconfig:RDB");
  std::vector<ConfigObject> objects;
  db.get(class_name, objects);  // get all objects of class "Segment"
  std::cout << "There are " << objects.size() << " objects of " << class_name << " class\n";
}
catch(daq::config::NotFound& ex) {
  std::cerr << "Wrong database schema, caught " << ex << ". Check the right database is used!\n";
}
catch(daq::config::Exception& ex) {
  std::cerr << "Caught exception " << ex << std::endl;
}

For corresponding generated data access library template method get() the daq::config::NotFound exception is not thrown. In case if a database with wrong schema is loaded, the problem is reported by daq::config::Generic exception:

try {
  Configuration db("rdbconfig:RDB");
  std::vector<const daq::core::Segment*> objs;
  db.get(objs);  // get all objects of class "Segment"
  std::cout << "There are " << objects.size() << " objects of Segment class\n";
}
catch(daq::config::Exception& ex) {
  std::cerr << "Caught exception " << ex << std::endl;
}
Example 4. Access object's attributes and relationships

In the past it was necessary to check boolean status of generated DAL set method and get/set status of config layer methods dealing with attributes and relationships. Now all such methods are void and throw generic daq::config::Exception in case of problems, e.g.:

::ConfigObject obj = ...;
std::string name;
if(obj.get("Name", name) == false) { std::cerr << "ERROR: cannot read Name attribute" << std::endl; }
ConfigObject item;
if(obj.get("Item", item) == false) { std::cerr << "ERROR: cannot read Item relationship" << std::endl; }
daq::core::Application * a = ...;
name = a->get_Name();
const daq::core::Item * i = a->get_Item();

Now such code needs to be changed to the following:

try {
  ::ConfigObject obj = ...;
  std::string name; obj.get("Name", name);
  ConfigObject item; obj.get("Item", item);
  daq::core::Application * a = ...;
  name = a->get_Name();
  const daq::core::Item * i = a->get_Item();
}
catch (daq::config::Exception& ex) {
  std::cerr << "Caught exception " << ex << std::endl;
}

Modify Update-on-notification

When receive a notification, update objects of derived classes stored in the clients configuration cache (this problem has been found in the Run Control). Note, the update of direct and base classes in case of notification was implemented before.

Below there are several examples explaining different mechanisms of objects updates in client's cache depending on subscription:

  1. class B is derived from class A, class C is derived from class B;
  2. there are objects a, b, and c created from corresponding classes A, B and C;
  3. all objects a, b and c are read into client's configuration cache, i.e.
  4. all objects a, b and c been updated;
  5. client subscribes on one notification of all objects of class A, B or C (e.g. subscribe on all changes of objects in class B).
Example of direct classes update (implemented in previous release)
Example of base classes update (implemented in previous release)
Example of derived classes update (new, was missing in previous releases)

Thus, subscription on:

Add Configuration unread_objects() Method

Add method Configuration::unread_objects() to unread unread objects of template classes in the client's configuration cache. This is required after reading parameters for substitution, since cache contains objects with non-substituted attributes.

Changes in the tdaq-01-06-02 Release

Also note config changes in the LST release.