10 Nov 2025, Konstantin Olchanski, Info, db_delete_key(TRUE)
|
> I would handle this actually like symbolic links are handled under linux. If you delete a symbolic link, the link gets
> detected and NOT the file the link is pointing to.
>
> So I conclude that the "follow links" is a misconception and should be removed.
I am finally writing test code for the ODB API, partially to discover what db_delete_key(TRUE) actually does,
because when I went ahead to remove it, I found that it's use is inconsistent. And indeed
it does strange and unexpected things, I will proceed with removing it.
K.O. |
17 Nov 2025, Konstantin Olchanski, Bug Report, broken scroll on midas web pages
|
> Sorry about that. I could not figure out what was the reason for doing this. This was during the time I was working on the file_picker. I removed these lines and see no effect on the file_picker. I'll continue checking it affect anything else.
I confirm reported problem seems to be fixed in commit:
https://bitbucket.org/tmidas/midas/commits/7f2690b478d6dfb16b48fc98955093e6369b04c1
Big thanks to Stefan and Zaher for figuring it out quickly.
K.O. |
20 Nov 2025, Konstantin Olchanski, Info, db_delete_key(TRUE)
|
> > I would handle this actually like symbolic links are handled under linux. If you delete a symbolic link, the link gets
> > detected and NOT the file the link is pointing to.
> >
> > So I conclude that the "follow links" is a misconception and should be removed.
>
> I am finally writing test code for the ODB API, partially to discover what db_delete_key(TRUE) actually does,
> because when I went ahead to remove it, I found that it's use is inconsistent. And indeed
> it does strange and unexpected things, I will proceed with removing it.
>
this is now merged into develop. there will be deprecation warnings from mvodb and from midas_c_compat, I and Ben will clean
them up, just not today.
for more detail, see separate message.
K.O. |
20 Nov 2025, Konstantin Olchanski, Bug Fix, ODB update, branch feature/db_delete_key merged into develop
|
In the darkside vertical slice midas daq, we observed odb corruption which I
traced to db_delete_key(). cause of corruption is not important. important is to
have a robust odb where small corruption will stay localized and will not
require erasing corrupt odb and reloading it from a backup file.
To help debug such corruption one can try to set ODB "/Experiment/Protect ODB"
to "yes". This will make ODB shared memory read-only and user code scribbling
into the wrong memory address will cause a seg fault and core dump instead of
silent ODB corruption. This feature is not enabled by default because changing
ODB shared memory mapping from "read-only" to "writable" (and back) is not very
fast and it slows down MIDAS noticably.
MIDAS right before this merge was tagged "midas-2025-11-a", if you see this ODB
update cause trouble, please report it here and revert to this tagged version.
Updates:
- harden db_delete_key() against internal corruption, if odb inconsistency is
detected, do a clean crash instead of trying to delete stuff and corrupting odb
to the point where it has to be erased and reloaded from a backup file.
- additional refactoring to separate read-locked and write-locked code.
- merge of missing patch to avoid odb corruption when key area becomes 100% full
(or was it the data area? I forget now, I fixed one of them long time ago, now
both are fixed).
- remove the "follow_links" argument from db_delete_key(), see separate
discussion on this.
- add db_delete() to delete things by ODB path not by hkey (atomic fused
together db_find_link() and db_delete_key()).
- fixes for incorrect use of db_find_key() and db_delete_key(), this
unexpectedly follows symlinks and deletes the wrong ODB entry. (should have been
db_find_link(), now replaced with atomic db_delete()).
K.O. |
20 Nov 2025, Konstantin Olchanski, Info, switch midas to c++17
|
> > Following discussions at the MIDAS workshop, we propose to move MIDAS from c++11 to c++17.
> We shall move forward with this change.
It is done. Last c++11 MIDAS is midas-2025-11-a (plus the db_delete_key merge).
I notice the cmake does not actually pass "-std=c++17" to the c++ compiler, and on U-20, it is likely
the default c++14 is used. cmake always does the wrong thing and this will need to be fixed later.
K.O. |
20 Nov 2025, Konstantin Olchanski, Info, removal of ROOT support in mlogger
|
> > we should finally bite the bullet and remove ROOT support from MIDAS ...
as discussed, HAVE_ROOT and OBSOLETE were removed from mlogger. rmana and ROOT support in manalyzed remain,
untouched.
last rmlogger is in MIDAS tagged midas-2025-11-a.
K.O. |
21 Nov 2025, Konstantin Olchanski, Info, cppcheck
|
> (rules for running cppcheck have gone missing, I hope I find them).
found them. I built cppcheck from sources.
520 ~/git/cppcheck/build/bin/cppcheck src/midas.cxx
523 ~/git/cppcheck/build/bin/cppcheck manalyzer/manalyzer.cxx manalyzer/mjsroot.cxx
524 ~/git/cppcheck/build/bin/cppcheck src/tmfe.cxx
525 ~/git/cppcheck/build/bin/cppcheck midasio/*.cxx
526 ~/git/cppcheck/build/bin/cppcheck mjson/*.cxx
K.O. |
25 Nov 2025, Konstantin Olchanski, Bug Fix, fixed db_find_keys()
|
Function db_find_keys() added by person unnamed in April 2020 never worked correctly, it is now fixed,
repaired, also unsafe strcpy() replaced by mstrlcpy().
This function is used by msequencer ODBSet function and by odbedit "set" command.
Under all conditions it returned DB_NO_KEYS, only two use cases actually worked:
set runinfo/state 1 <--- no match pattern - works
set run*/state 1 <--- match multiple subdirectories - works
set runinfo/stat* 1 <--- bombs out with DB_NO_KEY
set run*/stat* 1 <--- bombs out with DB_NO_KEY
All four use cases now work.
commit b5b151c9bc174ca5fd71561f61b4288c40924a1a
K.O. |
25 Nov 2025, Konstantin Olchanski, Bug Fix, ODB update, branch feature/db_delete_key merged into develop
|
> Thanks for the fixes, which I all approve.
>
> There is still a "follow_links" in midas_c_compat.h line 70 for Python. Probably Ben has to look into that. Also
> client.py has it.
Correct, Ben will look at this on the python side.
And I will be updating mvodb soon and fix it there.
K.O. |
25 Nov 2025, Konstantin Olchanski, Bug Report, Cannot edit values in a subtree containing only a single array of BOOLs using the ODB web interface
|
> Thanks --- it looks like this commit resolves the issue for us ...
> Thanks to Ben Smith for pointing us at exactly the right commit
I would like to take the opportunity to encourage all to report bug fixes like this one to this mailing list.
This looks like a serious bug, many midas users would like to know when it was introduced, when found, when fixed
and who takes the credit.
K.O. |
25 Nov 2025, Konstantin Olchanski, Info, switch midas to c++17
|
>
> > I notice the cmake does not actually pass "-std=c++17" to the c++ compiler, and on U-20, it is likely
> > the default c++14 is used. cmake always does the wrong thing and this will need to be fixed later.
>
> set(CMAKE_CXX_STANDARD 17)
> set(CMAKE_CXX_STANDARD_REQUIRED ON)
>
We used to have this, it is not there now.
>
> Get it to do the right thing?
>
Unlikely, as Stefan reported, asking for C++17 yields -std=gnu++17 which is close enough, but not the same
thing.
For now, it does not matter, U-22 and U-24 are c++17 by default, if somebody accidentally commits c++20
code, builds will fail and somebody will catch it and complain, plus the weekly bitbucket build will bomb-
out.
On U-20, default is c++14 and builds will start bombing out as soon as we commit some c++17 code.
el7 builds have not worked for some time now (a bizarre mysterious error)
el8, el9, el10 likely same situation as Ubuntu.
macos, not sure.
K.O. |
25 Nov 2025, Konstantin Olchanski, Info, switch midas to c++17
|
> target_compile_features(<target> PUBLIC cxx_std_17)
> which correctly causes a
> c++ -std=gnu++17 ...
I think this is set in a couple of places, yet I do not see any -std=xxx flag passed to the compiler.
(and I am not keen on spending a full day fighting cmake)
(btw, -std=c++17 and -std=gnu++17 are not the same thing, I am not sure how well GNU extensions are supported on
macos)
K.O. |
25 Nov 2025, Konstantin Olchanski, Suggestion, manalyzer root output file with custom filename including run number
|
Hi, Jonas, thank you for reminding me about this. I hope to work on manalyzer in the next few weeks and I will review the ROOT output file name scheme.
K.O.
> Hi all,
>
> Could you please get back to me about whether something like my earlier suggestion might be considered, or if I should set up some workaround to rename files at EOR for our experiments?
>
> https://daq00.triumf.ca/elog-midas/Midas/3042 :
> -----------------------------------------------
> > Hi all,
> >
> > Would it be possible to extend manalyzer to support custom .root file names that include the run number?
> >
> > As far as I understand, the current behavior is as follows:
> > The default filename is ./root_output_files/output%05d.root , which can be customized by the following two command line arguments.
> >
> > -Doutputdirectory: Specify output root file directory
> > -Ooutputfile.root: Specify output root file filename
> >
> > If an output file name is specified with -O, -D is ignored, so the full path should be provided to -O.
> >
> > I am aiming to write files where the filename contains sufficient information to be unique (e.g., experiment, year, and run number). However, if I specify it with -O, this would require restarting manalyzer after every run; a scenario that I would like to avoid if possible.
> >
> > Please find a suggestion of how manalyzer could be extended to introduce this functionality through an additional command line argument at
> > https://bitbucket.org/krieger_j/manalyzer/commits/24f25bc8fe3f066ac1dc576349eabf04d174deec
> >
> > Above code would allow the following call syntax: ' ./manalyzer.exe -O/data/experiment1_%06d.root --OutputNumbered '
> > But note that as is, it would fail if a user specifies an incompatible format such as -Ooutput%s.root .
> >
> > So a safer, but less flexible option might be to instead have the user provide only a prefix, and then attach %05d.root in the code.
> >
> > Thank you for considering these suggestions! |
25 Nov 2025, Konstantin Olchanski, Suggestion, Improve process for adding new variables that can be shown in history plots
|
> One aspect of the MIDAS history plotting I find frustrating is the sequence for adding a new history
> variable and then plotting them. ...
this has been a problem in MIDAS for a very long time, we have tried and failed to fix/streamline/improve
it many times and obviously failed. many times.
this is what must happen when adding a new history variable:
1) new /eq/xxx/variables/vvv entry must show up in ODB
1a) add the code for the new data to the frontend
1b) start the frontend
1c) if new variable is added in the frontend init() method, it will be created in ODB, done.
1d) if new variable is added by the event readout code (i.e. via MIDAS event data bank automatically
written to ODB by RO_ODB flags), then we need to start a run.
1e) if this is not periodic event, but beam event or laser event or some other triggered event, we must
also turn on the beam, turn on the laser, etc.
1z) observe that ODB entry exists
3) mlogger must discover this new ODB entry:
3a) mlogger used to rescan ODB each time something in ODB changes, this code was removed
3b) mlogger used to rescan ODB each time a new run is started, this code was removed
3c) mlogger rescans ODB each time it is restarted, this still works.
so sequence is like this: modify, restart frontend, starts a run, stop the run, observe odb entry is
created, restart mlogger, observe new mhf files are created in the history directory.
4) mhttpd must discover that a new mhf file now exists, read it's header to discover history event and
variable names and make them available to the history panel editor.
it is not clear to me that this part currently works:
4a) mhttpd caches the history event list and will not see new variables unless this cache is updated.
4b) when web history panel editor is opened, it is supposed to tell mhttpd to update the cache. I am
pretty sure it worked when I wrote this code...
4c) but obviously it does not work now.
restarting mhttpd obviously makes it load the history data anew, but there is no button to make it happen
on the MIDAS web pages.
so it sounds like I have to sit down and at least retest this whole scheme to see that it works at least
in some way.
then try to improve it:
a) the frontend dance in (1) is unavoidable
b) mlogger must be restarted, I think Stefan and myself agree on this. In theory we could add a web page
button to call an mlogger RPC and have it reload the history. but this button already exists, it's called
"restart mlogger".
c) newly create history event should automatically show up in the history panel editor without any
additional user action
d) document the two intermediate debugging steps:
d1) check that the new variable was created in ODB
d2) check that mlogger created (and writes to) the new history file
this is how I see it and I am open to suggestion, changes, improvements, etc.
K.O. |
27 Nov 2025, Konstantin Olchanski, Suggestion, mvodb WS and family type matching
|
> This is not a bug per se, but I find it a little odd that the MVOdb functions RS,
> RSA, RSAI, and WSA use std::string as their type, while WS ans WSAI use const
> char*
>
> Seems to me like simple overloading a la
> void WS(const char* varname, const std::string v, MVOdbError* error = NULL){
> WS(varname, v.c_str(), v.size(), error);
> }
>
> should be all that's needed, right?
No short answer to this one.
This situation is an excellent example of c++ bloat. Reduced to bare basics:
1) "naive" c++ code:
void foo(std::string xxx) { ... };
int main() { foo("bar"); }
nominally:
a new string object is created to hold "bar"
a new string object is copy-created to pass it as argument to foo()
result:
two object creations (two calls to malloc + constructors)
plus memcpy() of string data. (compiler may or may not optimize the 2nd string)
2) "advanced" c++ code:
void foo(const std::string& xxx) { ... };
int main() { foo("bar"); }
copy-created 2nd string is avoided, but string object to hold "bar" is still must be
made, 1 malloc(), 1 memcpy().
3) "pure C" code:
void foo(const char xxx) { ... };
int main() { foo("bar"); }
address of "bar" (placed in read-only memory) is passed in a register, no malloc(), no
memcpy(), nada, zilch.
One can argue that bloat does not matter, "just buy a bigger computer".
This ignores the fact that malloc() is quite expensive, nominally requires taking a
mutex, and suddenly multiple threads calling foo() are unexpectedly serialized against
the malloc() internal mutex.
I guess you can have an advanced malloc() that uses per-thread memory pools, but now
instead of deterministic "always take a lock", we have non-deterministic "take a lock
sometimes, when per-thread memory pools decide to jockey for more memory".
This type of non-deterministic behaviour is bad for real-time applications.
Ultimately it boils down to personal style, I prefer "C-like" efficiency and
transparency, when I call foo() it is obvious there will be no hidden malloc(), no
hidden mutex.
I guess mvodb could have "const std::string&" version of each "const char*" function,
as if there is too few functions there already...
This problem is not isolated to mvodb, but pertains to any API, including midas.h.
I would say, if most function calls are foo("abc"); then "const char*" version is
sufficient, if most calls are foo(string + "something"); then "const std::string&" is
more appropriate.
K.O. |
27 Nov 2025, Konstantin Olchanski, Bug Report, Error(?) in custom page documentation
|
the double-decode bug strikes again!
> This commit breaks the sequencer pages...
>
> > Indeed a bug. Fixed in commit
> >
> > https://bitbucket.org/tmidas/midas/commits/5c1133df073f493d74d1fc4c03fbcfe80a3edae4
> >
> > Stefan |
27 Nov 2025, Konstantin Olchanski, Suggestion, Improve process for adding new variables that can be shown in history plots
|
> > I assume that mlogger rescanning ODB is somewhat intensive process; and that's why we don't want rescanning to
> > happen every time the ODB is changed?
>
> A rescan maybe takes some tens of milliseconds. Something you can do on every run, but not on every ODB change (like writing to the slow control values).
> We would need a somehow more clever code which keeps a copy of the variable names for each equipment. If the names change or the array size changes,
> the scan can be triggered.
>
That's right, scanning ODB for history changes is essentially free.
Question is what do we do if something was added or removed.
I see two ways to think about it:
1) history is independent from "runs", we see a change, we apply it (even if it takes 10 sec or 2 minutes).
2) "nothing should change during a run", we must process all changes before we start a run (starting a run takes forever),
and we must ignore changes during a run (i.e. updated frontend starts to write new data to history). (this is why
the trick to "start a new run twice" used to work).
>
> > Stopping/restarting mlogger is okay. But would it be better to have some alternate way to force mlogger to
> > rescan the ODB?
>
It is "free" to rescan ODB every 10 second or so. Then we can output a midas message "please restart the logger",
and set an ODB flag, then when user opens the history panel editor, it will see this flag
and tell the user "please restart the logger to see the latest changes in history". It can even list
the specific changes, if we want ot be verbose about it.
>
> Indeed. But whatever "new" we design for the scan will users complain "last week it was enough to restart the logger, now what do I have to do". So nothing
> is perfect. But having a button in the ODB editor like "Rebuild history database" might look more elegant. One issue is that it needs special treatment, since
> the logger (in the Mu3e experiment) needs >10s for the scan, so a simple rpc call will timeout.
>
I like the elegance of "just restart the logger".
Having a web page button to tell logger to rescan the history is cumbersome technically,
(web page calls mjsonrpc to mhttpd, mhttpd calls a midas rpc to mlogger "please set a flag to rescan the history",
then web page polls mhttpd to poll mlogger for "are you done yet?". or instead of polling,
deal with double timeouts, in midas rpc to mlogger and mjsronrpc timeout in javascript).
And to avoid violating (2) above, we must tell user "you cannot push this button during a run!".
I say, let's take the low road for now and see if it's good enough:
a) have the history system report any changes in midas.log - "history event added", "new history variable added" (or "renamed"),
this will let user see that their changes to the equipment frontend "took" and flag any accidental/unwanted changes.
b) have mlogger periodically scan ODB and set a "please restart me" flag. observe this flag in the history editor
and tell the user "please restart the logger to see latest changes in the history".
K.O. |
27 Nov 2025, Konstantin Olchanski, Info, switch midas to c++17
|
>
> set(CMAKE_CXX_STANDARD 17)
> set(CMAKE_CXX_STANDARD_REQUIRED ON)
> set(CMAKE_CXX_EXTENSIONS OFF) # optional: disables GNU extensions
>
Looks like it works, I see -std=c++17 everywhere. Added same to manalyzer and mscb (mscb was still c++11).
Build on U-20 works (g++ accepts -std=c++17), build on CentOS-7 bombs, cmake 3.17.5 does not know CXX17.
K.O. |
27 Nov 2025, Konstantin Olchanski, Forum, Control external process from inside MIDAS
|
> Rather than investing time to re-invent the wheel here, better try to modify your EPICS driver process to
become a midas process.
I am with Stefan on this. Quite a bit of work went into the tmfe c++ framework to make it easy/easier to do
this - take an existing standalone c/c++ program and midas-ize it: in main(), "just add" calls to connect to
midas and to start the midas threads - rpc handler, watchdog, etc.
Alternatively, one can write a midas "stdout+stderr bridge", and start your standalone program
from the programs page like this:
myprogram |& cm_msg_bridge --name "myprogram" (redirect both stdout and stderr to cm_msg_bridge stdin)
cm_msg_bridge would read stdin and put them in cm_msg(). it will connect to midas using the name "myprogram"
to make it show "green" on the status page and it will be stoppable from the programs page.
care will need to be taken for myprogram to die cleanly when stdout and stderr are closed after cm_msg_bridge
exits.
K.O. |
14 Feb 2020, Konrad Briggl, Forum, Writting Midas Events via FPGAs
|
Hello Stefan,
is there a difference for the later data processing (after writing the ring buffer blocks)
if we write single events or multiple in one rb_get_wp - memcopy - rb_increment_wp cycle?
Both Marius and me have seen some inconsistencies in the number of events produced that is reported in the status page when writing multiple events in one go,
so I was wondering if this is due to us treating the buffer badly or the way midas handles the events after that.
Given that we produce the full event in our (FPGA) domain, an option would be to always copy one event from the dma to the midas-system buffer in a loop.
The question is if there is a difference (for midas) between
[pseudo code, much simplified]
while(dma_read_index < last_dma_write_index){
if(rb_get_wp(pdata)!=SUCCESS){
dma_read_index+=event_size;
continue;
}
copy_n(dma_buffer, pdata, event_size);
rb_increment_wp(event_size);
dma_read_index+=event_size;
}
and
while(dma_read_index < last_dma_write_index){
if(rb_get_wp(pdata)!=SUCCESS){
...
};
total_size=max_n_events_that_fit_in_rb_block();
copy_n(dma_buffer, pdata, total_size);
rb_increment_wp(total_size);
dma_read_index+=total_size;
}
Cheers,
Konrad
> The rb_xxx function are (thoroughly tested!) robust against high data rate given that you use them as intended:
>
> 1) Once you create the ring buffer via rb_create(), specify the maximum event size (overall event size, not bank size!). Later there is no protection any more, so if you obtain pdata from rb_get_wp, you can of course write 4GB to pdata, overwriting everything in your memory, causing a total crash. It's your responsibility to not write more bytes into pdata then
> what you specified as max event size in rb_create()
>
> 2) Once you obtain a write pointer to the ring buffer via rb_get_wp, this function might fail when the receiving side reads data slower than the producing side, simply because the buffer is full. In that case the producing side has to wait until space is freed up in the buffer by the receiving side. If your call to rb_get_wp returns DB_TIMEOUT, it means that the
> function did not obtain enough free space for the next event. In that case you have to wait (like ss_sleep(10)) and try again, until you succeed. Only when rb_get_wp() returns DB_SUCCESS, you are allowed to write into pdata, up to the maximum event size specified in rb_create of course. I don't see this behaviour in your code. You would need something
> like
>
> do {
> status = rb_get_wp(rbh, (void **)&pdata, 10);
> if (status == DB_TIMEOUT)
> ss_sleep(10);
> } while (status == DB_TIMEOUT);
>
> Best,
> Stefan
>
>
> > Dear all,
> >
> > we creating Midas events directly inside a FPGA and send them off via DMA into the PC RAM. For reading out this RAM via Midas the FPGA sends as a pointer where it has written the last 4kB of data. We use this pointer for telling the ring buffer of midas where the new events are. The buffer looks something like:
> >
> > // event 1
> > dma_buf[0] = 0x00000001; // Trigger and Event ID
> > dma_buf[1] = 0x00000001; // Serial number
> > dma_buf[2] = TIME; // time
> > dma_buf[3] = 18*4-4*4; // event size
> > dma_buf[4] = 18*4-6*4; // all bank size
> > dma_buf[5] = 0x11; // flags
> > // bank 0
> > dma_buf[6] = 0x46454230; // bank name
> > dma_buf[7] = 0x6; // bank type TID_DWORD
> > dma_buf[8] = 0x3*4; // data size
> > dma_buf[9] = 0xAFFEAFFE; // data
> > dma_buf[10] = 0xAFFEAFFE; // data
> > dma_buf[11] = 0xAFFEAFFE; // data
> > // bank 1
> > dma_buf[12] = 0x1; // bank name
> > dma_buf[12] = 0x46454231; // bank name
> > dma_buf[13] = 0x6; // bank type TID_DWORD
> > dma_buf[14] = 0x3*4; // data size
> > dma_buf[15] = 0xAFFEAFFE; // data
> > dma_buf[16] = 0xAFFEAFFE; // data
> > dma_buf[17] = 0xAFFEAFFE; // data
> >
> > // event 2
> > .....
> >
> > dma_buf[fpga_pointer] = 0xXXXXXXXX;
> >
> >
> > And we do something like:
> >
> > while{true}
> > // obtain buffer space
> > status = rb_get_wp(rbh, (void **)&pdata, 10);
> > fpga_pointer = fpga.read_last_data_add();
> >
> > wlen = last_fpga_pointer - fpga_pointer; \\ in 32 bit words
> > copy_n(&dma_buf[last_fpga_pointer], wlen, pdata);
> > rb_status = rb_increment_wp(rbh, wlen * 4); \\ in byte
> >
> > last_fpga_pointer = fpga_pointer;
> >
> > Leaving the case out where the dma_buf wrap around this works fine for a small data rate. But if we increase the rate the fpga_pointer also increases really fast and wlen gets quite big. Actually it gets bigger then max_event_size which is checked in rb_increment_wp leading to an error.
> >
> > The problem now is that the event size is actually not to big but since we have multi events in the buffer which are read by midas in one step. So we think in this case the function rb_increment_wp is comparing actually the wrong thing. Also increasing the max_event_size does not help.
> >
> > Remark: dma_buf is volatile so memcpy is not possible here.
> >
> > Cheers,
> > Marius |
|