Back Midas Rome Roody Rootana
  Midas DAQ System, Page 59 of 162  Not logged in ELOG logo
New entries since:Wed Dec 31 16:00:00 1969
Entry  16 Jul 2026, Konstantin Olchanski, Info, c++ exceptions, follow up 
Bjarne Stroustrup gave a nice talk at CERN, good enough to bring Rene Brun out of retirement to ask a few 
questions at the 1:16:50 mark.

https://indico.cern.ch/event/1696048/
https://videos.cern.ch/record/3026440

Good listen, but closer to home:

Stroustrup slides have a wee reference to a CppCon-2024 video "C++ exceptions for smaller firmware", I gave 
it a listen. The guy talks fast and waves hands vigorously, but his material is solid.

In the first 30 minutes he busts the myth that embedded programming can not/should not use exceptions (with 
numbers and example code).

In the second 30 minutes he demonstrates the insane code complexity required to implement exceptions, and 
that also makes per-call error checking very cheap (compares all three "return bool", "return 
std::expected", "throw exception", with disassembly of example code)

The the third 30 minutes he talks about project level implications of using exceptions vs other methods of 
error handling (one example is audacity).

If you did not want to use exceptions before watching this talk, you will after!

https://www.youtube.com/watch?v=bY2FlayomlE

K.O.
    Reply  16 Jul 2026, Konstantin Olchanski, Info, c++ exceptions, follow up 
> If you did not want to use exceptions before watching this talk, you will after!

Not me. One issue, exceptions are unpredictable, for example, does this code always print "hello, world"?

#include <stdio.h>
#include "foo.h"
int main(...)
{
  foo();
  printf("hello, world\n");
  return 0;
}

I can wrap foo() (and each and every function call) in try/catch.

I can check if today's version of foo.h declares foo() as "nothrow" (but what if tomorrow's foo() start 
throwing?)

Unpredictable code can be dangerous:

void boil_kettle()
{
   i2c_set_bit(10, 1); // bit 10 is stove heater control, turn it on
   foo(); // wait for kettle to start boiling
   i2c_set_bit(10, 0); // turn heater off
}

If foo() starts throwing exceptions, I will likely have a fire in my kitchen!

K.O.
    Reply  16 Jul 2026, Yiwen Yang, Info, c++ exceptions, follow up 
Thanks for sharing the videos, Konstantin.

> Unpredictable code can be dangerous:
> 
> void boil_kettle()
>
{
>    i2c_set_bit(10, 1); // bit 10 is stove heater control, turn it on
>    foo(); // wait for kettle to
start boiling
>    i2c_set_bit(10, 0); // turn heater off
> }
> 
> If foo() starts throwing exceptions, I
will likely have a fire in my kitchen!
> 

Indeed, that would certainly be problematic. However, there is a
way to get around this while not having to wrap everything in try ... catch ... blocks with more C++
machinery using the catch-all handler and RAII.

To quickly expand upon your example:


=== main.cpp ===


#include <iostream>
#include <unistd.h>

void i2c_set_bit(int bit, int val) {
	std::cout << "Bit " << bit <<
" set to " << val << "\n";
}

class i2c_temp_bit {
	int bit; /* which bit */
	int newval; /* value to set bit
to */
	int oldval; /* value to reset bit to */
public:
	i2c_temp_bit(int b, int nv, int ov)
		: bit{b},
newval{nv}, oldval{ov}
	{
		std::cout << "Bit " << bit << " set to " << newval << "\n";
	}
	~i2c_temp_bit() 

{
		std::cout << "Bit " << bit << " set to " << oldval << "\n";
	}
	i2c_temp_bit(const i2c_temp_bit&) =
delete;
	i2c_temp_bit(i2c_temp_bit&&) = delete;
	i2c_temp_bit& operator=(const i2c_temp_bit&) = delete;
	
i2c_temp_bit& operator=(i2c_temp_bit&&) = delete;
};

int check_kettle_temp() {
	static int callnum = 0;
	if
(++callnum > 5) {
		return 100;
	}
#ifdef BADNUM
	else if (callnum == 4) { /* Don't like this number */
		
throw callnum;
	}
#endif
	else {
		return 25;
	}
}

void foo() {
	std::cout << "Waiting for kettle to boil"
<< std::endl;
	do { /* Check every 100 ms */
		usleep(100000);
	} while (check_kettle_temp() < 100);
	
std::cout << "Water is boiled!" << std::endl;
}

void boil_kettle()
{
#ifdef RAII
	i2c_temp_bit temp(10, 1,
0);
	foo();
#else
	i2c_set_bit(10, 1); // bit 10 is stove heater control, turn it on
	foo(); // wait for
kettle to start boiling
	i2c_set_bit(10, 0); // turn heater off
#endif
}

int main()
{
	try {
		
boil_kettle();
	} catch (...) {
		std::cerr << "Abnormal termination\n";
	}
}

=== main.cpp ===


When we run
this:

$ g++ -o main main.cpp && ./main
Bit 10 set to 1
Waiting for kettle to boil
Water is boiled!
Bit 10
set to 0

This is as expected, of course.
But indeed as you say, when foo() can throw an exception, we get a
problem:

$ g++ -DBADNUM -o main main.cpp && ./main
Bit 10 set to 1
Waiting for kettle to boil
Abnormal
termination

The exception is caught by the catch-all handler, but because boil_kettle() never finishes
execution, bit 10 never gets reset to 0 and that's a big problem.

With RAII however, we get this instead:

$
g++ -DBADNUM -DRAII -o main main.cpp && ./main
Bit 10 set to 1
Waiting for kettle to boil
Bit 10 set to 0

Abnormal termination

Because the i2c_temp_bit object is destroyed upon unwinding the boil_kettle() function,
it calls the destructor which resets the bit.

For closure, it does behave identically to the non-RAII
implementation when there are no exceptions:

$ g++ -DRAII -o main main.cpp && ./main
Bit 10 set to 1
Waiting
for kettle to boil
Water is boiled!
Bit 10 set to 0


Personally, I don't like this as it adds way too much
complexity to the code in order to avoid C++ foot-guns (see how the copy and move constructors etc. need to
be deleted, or else requires a bit of thinking to implement properly). 
But, it is an alternative that exists
to safely handle these situations.
    Reply  16 Jul 2026, Yiwen Yang, Info, c++ exceptions, follow up main.cpp
Looks like the code formatting got messed up by the elog... here it is in the attachment instead.
    Reply  20 Jul 2026, Konstantin Olchanski, Info, c++ exceptions, follow up 
> Looks like the code formatting got messed up by the elog... here it is in the attachment instead.

unfortunately, your code does nothing because you do not call "i2c_set_bit(10, 1);".

if you add it in the destructor:

~i2c_temp_bit() {
std::cout << "Bit " << bit << " set to " << oldval << "\n";
i2c_set_bit(10, 0);
}

you will burn the kitchen down if "std::cout" and "<<" throw an exception (as we know they do). (I 
always use printf(), instead of c++ exceptions, it can throw the SIGPIPE signal, so main() must 
always have signal(SIGPIPE, SIG_IGN);).

also if usleep() throws an exception, the water does not get boiled to 100 degC, important for food 
safety.

also there is a logic error, if you are at a high enough elevation (Mount Everest), water starts 
boiling well below 100 degC, so the loop never ends, probably until all water is gone and the empty 
kettle is heated to 100 degC, at this point, both kettle and heater are probably damaged. the 
infinite loop should have some kind of safety limit.

K.O.
Entry  23 Jul 2026, Konstantin Olchanski, Info, ODB links to array elements explained 
this is a draft message, it will be updated with additional information. K.O.

Back in 2007, Stefan implemented the very useful feature, ODB links to array elements,
https://daq00.triumf.ca/elog-midas/Midas/418

This is handy for "Edit On Start", for history links, for feepics and other places.

If you have an array in ODB: 

ia5                             INT32   5     4     12m  0  RWD   
                                        [0]             10
                                        [1]             11
                                        [2]             12
                                        [3]             13
                                        [4]             14

a normal ODB link works as a UNIX filesystem symlink, whereas an ODB link that includes an array index refers to just one element of the link target array:

symlink_to_ia5_2 -> /test_odb/ia5[2]
                                INT32   1     4     12m  0  RWD   12

Because there was some confusion over how it works and how it is actually implements,
some ODB functions do not implement this feature consistently.

Here, I explain how it actually works:

0) expected syntax is: "link name" -> "/link/target/array[12345]":

- "/link/target/array" is the absolute ODB path to the link target (must start with "/", relative links are not permitted)
- array index "12345" should be an integer (converted using atoi())
- there should be nothing after final "]"
- there should be nothing between the "[" and the array index decimal numeric value

1) db_get_data():

- before calling db_get_data() we must call db_find_key()
- db_find_key() will resolve ODB links and return the final destination (or an error if dangling link or circular link or too many nested links)
- db_get_data() will return the data from this ODB key. this is the path for normal symlinks.

- if db_find_key() encounters an ODB link to an array element (target path contains "["), it returns this key (of type TID_LINK).
- db_get_data() checks the key type (normally it would be TID_INT, TID_STRING, etc). if it is TID_LINK, it means we have a link to an array index (as identified by db_find_key())
- in this case, ODB link is resolved using db_find_key(), array index is extracted from the link
- and db_get_data() returns data for the corresponding array element

If db_get_data() is called using an hKey returned by db_find_link() (instead of db_find_key())
and it happens to be an ODB link (TID_LINK), we have an inconsistent result:

- if it's a normal link, db_get_data() will return a type mismatch error
- db_get_data(TID_LINK) will return the link target string (NUL-terminated)
- if it's a link to an array element, db_get_data() will resolve it and return data from the link target array element.
- db_get_data(TID_LINK) will return a type mismatch error because it will resolve the link, then fail the type check as link target cannot be TID_LINK

2) db_get_value(), db_get_data_index()

- normal links work, resolved by db_find_key()
- links to array elements not implemented, will return a type mismatch error (i.e. TID_LINK vs user requested TID_STRING, TID_INT, etc)

3) mhttpd obsolete "jcopy"

- broken, returns malformed json (old code), returns the complete array, not just the linked array element (new code)

4) odbedit save odb.json

- broken, output file odb.json is empty (new code)

K.O.
Entry  14 Aug 2026, Julian Wollrath, Info, mplot.js: viridis colour scheme for 2D plots 
Dear all,

please do not be surprised if your 2D plots start looking different: The default
colour scheme changed to the (now kind of standard one) 'viridis' from
matplotlib to have perceptually uniform sequential colour scheme. So if you
print your plots in black and white or have problems colour vision deficiency
the plots should not have artificial artifacts anymore.


Cheers,
Julian
    Reply  14 Aug 2026, Stefan Ritt, Info, mplot.js: viridis colour scheme for 2D plots 20260814-172355.png
Here is an example how a 2D plot looks like.

Stefan
Entry  28 Sep 2004, Piotr Zolnierczuk, Forum, MIDAS/MVME167/Linux 
Hi,
 has anyone tried runnning midas frontend on a Linux running 
on a Motorola MVME167 motorola embedded CPU?
I have seen people running Linux on a MV167 
(http://www.sleepie.demon.co.uk/linuxvme/)
so in principle this can be done.

The reason I am asking is that we have a lot of them in house 
and we would like to avoid paying for VxWorks
(I have succesfully run Midas on a mvme167/VxWorks node)

Or maybe one has come up with a much better solution 
[short of dumping mv167 into a sewer :)]

Piotr
Entry  04 Nov 2004, Jan Wouters, Forum, Frontend code and the ODB 
I would like to know whether all parameters used by the frontend code have to be in the "Experiment/
Run Parameters" section.  This section can become big and difficult to maintain, because it is one single 
big section of experim.h (EXP_PARAM_DEFINED).  I have parameters the various frontends read at the 
beginning of each run, which set the hardware settings of various devices.  I would like to place these in 
a section all their own, organized by device.  Is this doable? 
    Reply  04 Nov 2004, Stefan Ritt, Forum, Frontend code and the ODB 
Hi Jan,

I usually keep under /Experiment/Run Parameters only those settings which are kind of "global" and thus of
interest to frontend *and* analyzer, like a run mode (data/calibration/cosmic/...). Settings more specific to a
frontend I keep under /Equipment/<name>/Settings where <name> is the equipment name the specific frontend
produces. In your case each frontend will then get its own tree (related to each fragment). Please note that
both discussed trees can contain a whole tree with subdirectories, which lets you organize your data better.

Best regards, Stefan.
Entry  25 Nov 2004, chris pearson, Forum, use of assert in mhttpd 
   We've had mhttpd aborting regularly since upgrading from midas-1.9.3.  This
happens during elog queries, and is due to an elog file that was incorrectly
modified by hand.  The modification to the file occurred 6 months ago.
   el_retrieve(midas.c:15683) now has several assert statements, one of which
aborts the program on reading the bad entry.

   Why is assert used, instead of an error return from the function (if
necessary), and maybe an error message in the log file?  Assert statements are
often removed, using NDEBUG, for normal use.

Chris

   The problem elog entry had one character removed, so end-of-file came before
the end of the message.  This could probably occur without the file being
altered, if the disk containing the elog fills.
    Reply  14 Dec 2004, Konstantin Olchanski, Forum, use of assert in mhttpd 
>    We've had mhttpd aborting regularly since upgrading from midas-1.9.3.  This
> happens during elog queries, and is due to an elog file that was incorrectly
> modified by hand.

(sorry for delayed reply, for reasons unknown, I did not get an email notice when this was posted)

Yes, I agree, error handling in midas elog code is insufficient (note missing error checks for
read() and lseek() system calls). Anything but "perfect" elog files would cause funny errors and
malfunctions.

>  The modification to the file occurred 6 months ago.
>    el_retrieve(midas.c:15683) now has several assert statements, one of which
> aborts the program on reading the bad entry.

I added those to fix problems with "broken last NN days" and with infinite looping in the elog code
that we observed in TWIST.

You are welcome to replace the assert() statements with proper error handling. I used to have some code
that could report the filename of the bad elog file. Can we also report the exact file location for broken
files.

Please send me the diff, I will commit it to midas cvs.

>    Why is assert used, instead of an error return from the function (if
> necessary), and maybe an error message in the log file?  Assert statements are
> often removed, using NDEBUG, for normal use.

I use assert() in several ways:

0) I want a core dump each time X happens. (This is the only reasonable action when facing memory/stack
corruption. The problems in the elog code were stack corruption).
1) "I am too lazy to write proper error handling code" so I just crash and burn. This includes the
case where "proper error handling" would be "too invasive".
2) the error is too bad (or too deep) and there is no reasonable way to recover. Print an error message
and dump core (for later analysis). I sometimes use "cm_msg(); abort()". (assert is "printf("error"); abort()")

Please refer to literature for philosophic discussions on uses of assert() (Argh! Stefan will have my
head again!), but I will mention that "abort() early, abort() often" I find very effective. BTW, this technique
is heavily used in the Linux kernel (oops(), bug(), panic()) with some good effect, too.

>    The problem elog entry had one character removed, so end-of-file came before
> the end of the message.  This could probably occur without the file being
> altered, if the disk containing the elog fills.

Yes, I think you are right. In TWIST, we have seen disk-full conditions break both elog and history.

K.O.
Entry  14 Dec 2004, Jan Wouters, Forum, Frontend index 
What is the api call to determine the index of the frontend when specifying the
-i parameter during execution of the frontend? 
    Reply  15 Dec 2004, Stefan Ritt, Forum, Frontend index 
> What is the api call to determine the index of the frontend when specifying the
> -i parameter during execution of the frontend? 

INT get_frontend_index();

- Stefan
Entry  15 Dec 2004, , Forum, Where's the definition of "H1_BOOK()" 
When i compile the experiment example of 1.9.5 the problem happened:

adccalib.c: In function `INT adc_calib_init()':
adccalib.c:114: `H1_BOOK' undeclared (first use this function)
adccalib.c:114: (Each undeclared identifier is reported only once for each
   function it appears in.)
make: *** [adccalib.o] Error 1

my ROOT is 4.01 and Zlib is 1.2.2
    Reply  15 Dec 2004, Pierre-Andre Amaudruz, Forum, Where's the definition of "H1_BOOK()" 
> When i compile the experiment example of 1.9.5 the problem happened:
> 
> adccalib.c: In function `INT adc_calib_init()':
> adccalib.c:114: `H1_BOOK' undeclared (first use this function)
> adccalib.c:114: (Each undeclared identifier is reported only once for each
>    function it appears in.)
> make: *** [adccalib.o] Error 1
> 
> my ROOT is 4.01 and Zlib is 1.2.2

We're in the process of fixing in the proper manner this problem, in the mean time
please add to the analyzer makefile the definition: -DUSE_ROOT at the line:
...
ROOTCFLAGS += -DHAVE_ROOT -DUSE_ROOT
Entry  16 Dec 2004, Jan Wouters, Forum, cm_msg 
Could someone please explain to me how cm_msg, cm_msg1, etc. all work.  The
documentation is very terse.  

I want to setup a fairly significant set of debugging, and error messages for a
new frontend.  I need to get these messages to a logging file.  I also would
like to get the error messages to the user through whatever interface Midas
normally uses for error reporting.  

Jan
    Reply  22 Dec 2004, Stefan Ritt, Forum, cm_msg 
> Could someone please explain to me how cm_msg, cm_msg1, etc. all work.  The
> documentation is very terse.  
> 
> I want to setup a fairly significant set of debugging, and error messages for a
> new frontend.  I need to get these messages to a logging file.  I also would
> like to get the error messages to the user through whatever interface Midas
> normally uses for error reporting.  

For errors, use

  cm_msg(MERROR, "routine_name", "Your error message, code=%d", i);

This produces an error message which is logged to midas.log, and distributed to all
clients which have called cm_msg_register(). For example odbedit will just print
that message. The syntax of the second half of cm_msg is the same as for printf(),
so you can add format specifiers and variable arguments as you do for printf(). The
first argument is the message type (MDEBUG for example is only distributed but not
logged). 

For a more detailed list of message types, please refer to

http://midas.triumf.ca/doc/html/AppendixE.html#midas_macro
Entry  11 Jul 2006, Razvan Stefan Gornea, Forum, Tundra Universe CA91C042 
I am not using Midas but I need some help from somebody experienced with VME access using the Tundra Universe, so I thought here I have a chance ...

I have a GE Fanuc 7700 and use the vme_universe driver (ver. 3.3). In the past I programed for a DAQ board using A24/D16. Now I have a new board using A24/MB and I am really last!

So the board has some 64-bit registers and some 32-bit registers (all aligned on 64-bit) and a FIFO to read the main data. After reading the user manual for universe chip and the docs for the driver I am still confused about how things are supposed to work.

First my understanding is that for reading 64-bit I need anyway the multiplex block mode. But nowhere I could find if the multiplex mode supports 32-bit transfers. Should I map two windows on the same VME address range, one for A24/D32 and one for A24/MB? Or read everything with an unsigned long long and cast to unsigned int all 32-bit registers?

Second I don't know how to handle the FIFO which is in the middle of the address range. When the board has a trigger I have to read more than 100000 times this FIFO. If I simply read at the FIFO address 100000 times do I get the VME multiplex block mode (if the window has been mapped with A24/MB address modifier)? How does the chip/driver know not to send the address and just do the data cycle after the first read?

I also had the naive idea to have a master window mapped on the board address range to access all the registers except the FIFO and to create a DMA buffer for the FIFO (FIFO readout is where most of the work is anyway so I guess an advantage is that will free the CPU) but it seems to me that the dma_transfer function in the kernel module increments the address. I don't dare change this since I don't even understand the exact relationship between accesses to the mapped window and what's happening on the VME bus.

Thanks for any help!
ELOG V3.1.6-083448f7