Odbxx: Difference between revisions

From MidasWiki
Jump to navigation Jump to search
m (Fixed typo)
(The midas documentation has moved to https://midas.triumf.ca/docs/)
Tag: Replaced
 
(20 intermediate revisions by 3 users not shown)
Line 1: Line 1:
A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.
The midas documentation has moved. See [https://midas.triumf.ca/docs/develop/api/cpp/odbxx/ Midas C++ API - midas::odb (odbxx)] for the new documentation about the odbxx C++ interface to the ODB.
 
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].
 
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.
 
== Basic usage ==
 
The simplest usage is like:
 
<pre>// Grab a bit of the ODB
midas::odb exp("/Experiment");
 
// Simple read
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;
 
// Make a change. The new value is automatically sent to the ODB.
// Most C++ operators are supported (++, += etc), or you can do a simple
// re-assignment like `exp["Transition timeout"] = 12345;`.
exp["Transition timeout"] += 100;
 
// Read the new value
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;
</pre>
 
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:
 
<pre>int curr_timeout = exp["Transition timeout"];</pre>
 
== Automatic refreshing ==
 
You may temporarily disable the automatic updating to/from the ODB
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.
 
If auto-refresh is enabled (the default), your new values are sent to
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB
is queried for new values whenever you access the value. In the above
example, the ODB is queried 4 times (during construction of <code>exp</code>, and
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1
time (when <code>exp["Transition timeout"]</code> is assigned to).
 
See the [[#Callback functions]] section below for details on how to have a function
called when a value changes.
 
== Arrays/vectors ==
 
ODB arrays are represented by std vectors.
 
You can access/edit individual elements using []:
 
<pre>odb["Example"][1] = 1.2;</pre>
 
You can completely re-assign content using a std::vector or std::array:
 
<pre>std::vector<float> vec = std::vector<float>(10);
odb["Example"] = vec;
</pre>
 
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,
it will be truncated; if shorter it will be extended with default values
(0 or an empty string).
 
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre>
 
Note that arithmetic operators are supported for arrays, and will apply
the operation to ALL ELEMENTS IN THE ARRAY:
 
<pre>// Create the vector
std::vector<float> vec = std::vector<float>(2);
vec[0] = 3;
vec[1] = 5;
 
// Assign in ODB
odb["Example"] = vec;
 
// Multiply ALL elements by 2
odb["Example"] *= 2;
 
// odb["Example"] now contains {6, 10}.
</pre>
 
You can directly iterate over arrays/vectors:
 
<pre>// Iterating using standard begin/end.
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {
  int val = *it;
  std::cout << val << std::endl;
}
</pre>
 
<pre>// Iterating using C++11 range-based for loop.
for (int val : o["Int Array"]) {
  std::cout << val << std::endl;
}
</pre>
 
== Strings ==
 
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code>
family of functions, where strings are returned as char*). You may have vectors of strings.
 
== Creating new bits of the ODB ==
 
You can automatically create bits of the ODB by passing a struct to the
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:
 
<pre>// Define the ODB structure
midas::odb new_bit = {
  {"Int32 Key", 42},
  {"Bool Key", true},
  {"Subdir", {
      {"Float key", 1.2f},    // floats must be explicitly specified
  }},
  {"Int Array", {1, 2, 3}},
  {"Double Array", {1.2, 2.3, 3.4}},
  {"String Array", {"Hello1", "Hello2", "Hello3"}},
  {"Large Array", std::array<int, 10>{} },  // array with explicit size
  {"Large String", std::string(63, '\0') },  // string with explicit size
};
 
// Then sync the structure. Any keys that don't exist will be created; any
// that already exist will keep their existing values...
o.connect("/Test/Settings");
 
// ... unless you make the `write_defaults` argument true, in which case the
// existing values will be ignored, and overwritten with what you specified above.
o.connect("/Test/Settings", true);
</pre>
 
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:
 
<pre>midas::odb existing_key("/MyExistingKey");
existing_key["MyNewSubKey"] = 1.23;
</pre>
 
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.
 
<pre>midas::odb existing_key("/MyExistingKey");
double val = existing_key["MyNewSubKey"](1.23);
</pre>
 
== Iterating over subkeys ==
 
You can use iterate over subkeys using normal iterator functions.
 
<pre>// Iterating using standard begin/end.
midas::odb exp("/Experiment");
 
for (auto it = exp.begin(); it != exp.end(); it++) {
  midas::odb& subkey = *it;
  std::cout << subkey.get_name() << " = " << subkey << std::endl;
}
</pre>
 
<pre>// Iterating using C++11 range-based for loop.
for (midas::odb& subkey : exp) {
  std::cout << subkey.get_name() << " = " << subkey << std::endl;
}
</pre>
 
You can check whether a subkey exists using <code>odb::is_subkey()</code>.
 
== Deleting bits of the ODB ==
 
You can use <code>odb::delete_key()</code> to remove part of the ODB:
 
<pre>midas::odb existing_bit("/Some/ODB/Path");
existing_bit.delete_key();
</pre>
 
== Callback functions ==
 
You may also set up callback functions that are called whenever a value
changes, using the <code>odb::watch()</code> function. Note that you must call
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it
is <code>cm_yield()</code> itself that calls your callback function.
 
The callback functions can either be a "normal" function or a C++ lambda.
In either case, it should accept one argument - a <code>midas::odb</code> object (passed
by reference) that contains the new state.
 
<pre>// Example with a lambda:
midas::odb to_watch("/Experiment");
to_watch.watch([](midas::odb &arg) {
  std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;
});
</pre>
 
<pre>// Example with a "normal" function:
void my_function(midas::odb &arg) {
  std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;
}
 
midas::odb to_watch("/Experiment");
to_watch.watch(my_function);
</pre>
 
== Example code ==
 
A full working example exploring most of the features can be found in
<code>progs/odbxx_test.cxx</code>. The test executable will be compiled as
<code>build/progs/odbxx_test</code> (it is not installed in the `bin` directory).

Latest revision as of 22:01, 30 October 2026

The midas documentation has moved. See Midas C++ API - midas::odb (odbxx) for the new documentation about the odbxx C++ interface to the ODB.