20 Mar 2025, Konstantin Olchanski, Bug Report, please fix mscb compiler warning
|
I am getting a scary compiler warning from MSCB code. I generally avoid touching MSCB code because I have no MSCB hardware to test it, so I am
asking the persons unnamed who wrote this code to fix it. Stock g++ on Ubuntu LTS 24.04. Thanks in advance!
In function ‘ssize_t read(int, void*, size_t)’,
inlined from ‘int mscb_interprete_file(const char*, unsigned char**, unsigned int*, unsigned char**, unsigned int*, unsigned char*)’ at
/home/olchansk/git/midas/mscb/src/mscb.cxx:2666:13:
/usr/include/x86_64-linux-gnu/bits/unistd.h:28:10: warning: ‘ssize_t __read_alias(int, void*, size_t)’ specified size 18446744073709551614
exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=]
28 | return __glibc_fortify (read, __nbytes, sizeof (char),
| ^~~~~~~~~~~~~~~
/usr/include/x86_64-linux-gnu/bits/unistd-decl.h: In function ‘int mscb_interprete_file(const char*, unsigned char**, unsigned int*, unsigned
char**, unsigned int*, unsigned char*)’:
/usr/include/x86_64-linux-gnu/bits/unistd-decl.h:29:16: note: in a call to function ‘ssize_t __read_alias(int, void*, size_t)’ declared with
attribute ‘access (write_only, 2, 3)’
29 | extern ssize_t __REDIRECT_FORTIFY (__read_alias, (int __fd, void *__buf,
| ^~~~~~~~~~~~~~~~~~
K.O. |
21 Mar 2025, Stefan Ritt, Bug Report, please fix mscb compiler warning
|
I hopefully fixed the waring (narrowing down from size_t to int). Please double check with your compiler.
Stefan |
25 Mar 2025, Konstantin Olchanski, Bug Report, please fix mscb compiler warning
|
> I hopefully fixed the waring (narrowing down from size_t to int). Please double check with your compiler.
Nope, it turns out complain is about the read() size argument, they really really really want it to be
size_t.
Looks like correct sequence is:
size_t size = file_size_somehow();
char* buf = (char*)malloc(size+1);
ssize_t rd = read(fd, buf, size);
if (rd < 0) { complain(); return; } // read() error
if ((size_t)rd != size) { complain(); return; } // cast to size_t is safe after we know rd >= 0
buf[rd] = 0; // make sure data is NUL terminated
What a mess. I want my UNIX V7 and K&R C back!
Perhaps I should add in system.cxx - ss_read_file(const char* filename, std::vector<char> &data);
Fixed it in mscb.cxx, odbedit.cxx, mhttpd.cxx and msequencer.cxx, commited, pushed.
U-24 builds without warnings. fwew...
K.O. |
20 Mar 2025, Konstantin Olchanski, Bug Report, please fix compiler warning
|
Unnamed person who added this clever bit of c++ coding, please fix this compiler warning. Stock g++ on Ubuntu LTS 24.04. Thanks in advance!
/home/olchansk/git/midas/src/system.cxx: In function ‘std::string ss_execs(const char*)’:
/home/olchansk/git/midas/src/system.cxx:2256:43: warning: ignoring attributes on template argument ‘int (*)(FILE*)’ [-Wignored-attributes]
2256 | std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
K.O. |
21 Mar 2025, Stefan Ritt, Bug Report, please fix compiler warning
|
> Unnamed person who added this clever bit of c++ coding, please fix this compiler warning. Stock g++ on Ubuntu LTS 24.04. Thanks in advance!
>
> /home/olchansk/git/midas/src/system.cxx: In function ‘std::string ss_execs(const char*)’:
> /home/olchansk/git/midas/src/system.cxx:2256:43: warning: ignoring attributes on template argument ‘int (*)(FILE*)’ [-Wignored-attributes]
> 2256 | std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
>
> K.O.
Replace the code with:
auto pclose_deleter = [](FILE* f) { pclose(f); };
auto pipe = std::unique_ptr<FILE, decltype(pclose_deleter)>(
popen(cmd, "r"),
pclose_deleter
);
Hope this is now warning-free.
Stefan |
25 Mar 2025, Konstantin Olchanski, Bug Report, please fix compiler warning
|
Confirming warning is fixed. Thanks! K.O.
> > Unnamed person who added this clever bit of c++ coding, please fix this compiler warning. Stock g++ on Ubuntu LTS 24.04. Thanks in advance!
> >
> > /home/olchansk/git/midas/src/system.cxx: In function ‘std::string ss_execs(const char*)’:
> > /home/olchansk/git/midas/src/system.cxx:2256:43: warning: ignoring attributes on template argument ‘int (*)(FILE*)’ [-Wignored-attributes]
> > 2256 | std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
> >
> > K.O.
>
> Replace the code with:
>
> auto pclose_deleter = [](FILE* f) { pclose(f); };
> auto pipe = std::unique_ptr<FILE, decltype(pclose_deleter)>(
> popen(cmd, "r"),
> pclose_deleter
> );
>
>
> Hope this is now warning-free.
>
> Stefan |
20 Mar 2025, Konstantin Olchanski, Bug Report, manalyzer module init order problem
|
Andrea Capra reported a problem with manalyzer module initialization order.
Original manalyzer design relies on the fact than module static constructors run in the same order as they
appear on the linker command line. This has been true for a very long time, but now we have evidence that on
Rocky Linux (unknown gcc version), this is no longer true.
The c++ standard does not define any specific order for static constructor in different source files.
(unlike C, C++ tends to treat the linker as something magical and ask the linker to do magical things).
The tmfe c++ modular frontend was designed after manalyzer and one design change is to explicitly construct
all objects from main(). (at the acceptable cost of extra boiler plate code).
One solution is to use GCC attribute "init_priority (priority)", see
https://stackoverflow.com/questions/211237/static-variables-initialisation-order
https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html
To use this, manalyzer module registration should be modified to read:
static TARegister tar __attribute__((init_priority(200))) (new ExampleCxxFactory);
A less magical solution is to change manalyzer design similar to tmfe where modules are constructed and
registered explicitely in the order specified by source code.
K.O.
P.S. I now ran out of time to test this and commit it to the documentation. It also need to be tested with
LLVM C++ compilers. |
25 Mar 2025, Konstantin Olchanski, Bug Report, manalyzer module init order problem
|
> Andrea Capra reported a problem with manalyzer module initialization order.
Permanent solution is now implemented.
In the analyzer module constructor (TARunObject), please set the value of fModuleOrder (default value is 0).
Modules with smaller fModuleOrder will run first, modules with bigger fModuleOrder will run last.
Please use the value range between -1 (built-in EventDump module) and 9999 (built-in InteractiveModule).
Modules with equal fModuleOrder will run in the same order as they were registered (std::stable_sort).
Example manalyzer modules and documentation README.md have been updated.
Linker command line and GCC attribute methods should also work, but I thought it better to provide explicit
programmatic control of module ordering.
P.S. the c++ tmfe frontend module design is newer and the problem of module (equipment) ordering is solved by
constructing them explicitly in main().
manalyzer commit:
commit 6ca130808cd05ead734450391bf6defc8335c04a (HEAD -> master, origin/master, origin/HEAD)
Author: Konstantin Olchanski <olchansk@daq00.triumf.ca>
Date: Tue Mar 25 15:53:13 2025 -0700
implement TARunObject::fModuleOrder to specify required ordering of analyzer modules
K.O. |
20 Mar 2025, Konstantin Olchanski, Bug Report, midas equipment "format"
|
we are migrating the dragon experiment from an old mac to a new mac studio and we ran into a problem
where one equipment format was set to "fixed" instead of "midas". lots of confusion, mdump crash,
analyzer crash, etc. (mdump fixes for this are already committed).
it made us think whether equipment format is still needed. in the old days we had choice of MIDAS and
YBOS formats, but YBOS was removed years ago, and I was surprised that format FIXED was permitted at
all.
I did a midas source code review, this is what I found:
- remnants of YBOS support in a few places, commit to remove them pending.
- FORMAT_ROOT is used in mlogger for automatic conversion of MIDAS banks to ROOT trees
- FORMAT_FIXED is used in a few slow control drivers in drivers/class, instead of creating MIDAS
banks, they copy raw data directly into an event (there is no bank header and no way to identify such
events automatically)
- lots of code to support different formats in mdump (mostly dead code)
- the rest of the code does not care or use this format stuff
Current proposal is to remove support for all formats except FORMAT_MIDAS (and FORMAT_ROOT in
mlogger).
- defines of FORMAT_XXX will be removed from midas.h
- "Format" will be removed from ODB Equipment/Common
- "Format" will be removed from ODB Logger/Channel
- to maintain binary compatibility, we can keep the "Format" ODB entries, but they will be ignored.
List of slow control drivers that support FORMAT_FIXED:
daq00:midas$ grep FORMAT_FIXED drivers/class/*
drivers/class/cd_fdg.cxx: if (fgd_info->format == FORMAT_FIXED) {
drivers/class/cd_ivc32.cxx: if (hv_info->format == XFORMAT_FIXED) {
drivers/class/cd_rf.cxx: if (rf_info->format == XFORMAT_FIXED)
drivers/class/generic.cxx: if (gen_info->format == XFORMAT_FIXED) {
drivers/class/hv.cxx: if (hv_info->format == XFORMAT_FIXED) {
drivers/class/multi.cxx: if (m_info->format == XFORMAT_FIXED) {
drivers/class/slowdev.cxx: if (gen_info->format == XFORMAT_FIXED) {
daq00:midas$
K.O. |
21 Mar 2025, Jonas A. Krieger, Bug Report, midas equipment "format"
|
Hi Konstantin,
In the PSI muSR laboratory, we are running about 140 slow control devices across six instruments using Format FIXED.
Could you please wait a bit with removing support for it so that we can assess if/how this will affect us?
Many thanks,
Jonas
> we are migrating the dragon experiment from an old mac to a new mac studio and we ran into a problem
> where one equipment format was set to "fixed" instead of "midas". lots of confusion, mdump crash,
> analyzer crash, etc. (mdump fixes for this are already committed).
>
> it made us think whether equipment format is still needed. in the old days we had choice of MIDAS and
> YBOS formats, but YBOS was removed years ago, and I was surprised that format FIXED was permitted at
> all.
>
> I did a midas source code review, this is what I found:
>
> - remnants of YBOS support in a few places, commit to remove them pending.
> - FORMAT_ROOT is used in mlogger for automatic conversion of MIDAS banks to ROOT trees
> - FORMAT_FIXED is used in a few slow control drivers in drivers/class, instead of creating MIDAS
> banks, they copy raw data directly into an event (there is no bank header and no way to identify such
> events automatically)
> - lots of code to support different formats in mdump (mostly dead code)
> - the rest of the code does not care or use this format stuff
>
> Current proposal is to remove support for all formats except FORMAT_MIDAS (and FORMAT_ROOT in
> mlogger).
>
> - defines of FORMAT_XXX will be removed from midas.h
> - "Format" will be removed from ODB Equipment/Common
> - "Format" will be removed from ODB Logger/Channel
> - to maintain binary compatibility, we can keep the "Format" ODB entries, but they will be ignored.
>
> List of slow control drivers that support FORMAT_FIXED:
>
> daq00:midas$ grep FORMAT_FIXED drivers/class/*
> drivers/class/cd_fdg.cxx: if (fgd_info->format == FORMAT_FIXED) {
> drivers/class/cd_ivc32.cxx: if (hv_info->format == XFORMAT_FIXED) {
> drivers/class/cd_rf.cxx: if (rf_info->format == XFORMAT_FIXED)
> drivers/class/generic.cxx: if (gen_info->format == XFORMAT_FIXED) {
> drivers/class/hv.cxx: if (hv_info->format == XFORMAT_FIXED) {
> drivers/class/multi.cxx: if (m_info->format == XFORMAT_FIXED) {
> drivers/class/slowdev.cxx: if (gen_info->format == XFORMAT_FIXED) {
> daq00:midas$
>
> K.O. |
25 Mar 2025, Konstantin Olchanski, Bug Report, midas equipment "format"
|
> In the PSI muSR laboratory, we are running about 140 slow control devices across six instruments using Format FIXED.
> Could you please wait a bit with removing support for it so that we can assess if/how this will affect us?
Roger. I believe as long as these data do not go into the MIDAS event data stream, you should not see any difference from
changing "FIXED" to "MIDAS", I hope you can test it and report how it works for you. If you see that things change in ODB
or in history, perhaps we can implement some kind of workaround to make the transition transparent.
K.O. |
19 Mar 2025, Zaher Salman, Forum, LabView-Midas interface
|
Hello,
Does anyone have experience with writing a MIDAS frontends to communicate with a device that operates using LabView (e.g. superconducting magnets, cryostats etc.). Any information or experience regarding this would be highly appreciated.
thanks,
Zaher |
19 Mar 2025, Konstantin Olchanski, Forum, LabView-Midas interface
|
> Does anyone have experience with writing a MIDAS frontends to communicate with a device that operates using LabView (e.g. superconducting magnets, cryostats etc.). Any information or experience regarding this would be highly appreciated.
Yes, in the ALPHA anti-hydrogen experiment at CERN we have been doing this since 2006.
Original system is very simple, labview side opens a TCP socket to the MIDAS felabview frontend
and sends the numeric data as an ASCII string. The first four chars of the data is the name
of the MIDAS data bank, second number is the data timestamp in seconds.
LCRY 1234567 1.1 2.2 3.3
A newer iteration is feGEM written by Joseph McKenna (member of this forum), it uses a more sophisticated
labview component. Please contact him directly for more information.
I can provide you with the source code for my original felabiew (pretty much unchanged from circa 2006).
K.O. |
20 Mar 2025, Zaher Salman, Forum, LabView-Midas interface
|
Thanks Konstantin. Please send me the felabview code or let me know where I can find it.
Zaher
> > Does anyone have experience with writing a MIDAS frontends to communicate with a device that operates using LabView (e.g. superconducting magnets, cryostats etc.). Any information or experience regarding this would be highly appreciated.
>
> Yes, in the ALPHA anti-hydrogen experiment at CERN we have been doing this since 2006.
>
> Original system is very simple, labview side opens a TCP socket to the MIDAS felabview frontend
> and sends the numeric data as an ASCII string. The first four chars of the data is the name
> of the MIDAS data bank, second number is the data timestamp in seconds.
>
> LCRY 1234567 1.1 2.2 3.3
>
> A newer iteration is feGEM written by Joseph McKenna (member of this forum), it uses a more sophisticated
> labview component. Please contact him directly for more information.
>
> I can provide you with the source code for my original felabiew (pretty much unchanged from circa 2006).
>
> K.O. |
20 Mar 2025, Konstantin Olchanski, Forum, LabView-Midas interface
|
> Thanks Konstantin. Please send me the felabview code or let me know where I can find it.
https://bitbucket.org/expalpha/a2daq/src/alpha/src/felabview.cxx
this is code circa 2006, there are now better ways to do some of that coding.
if you want bidirectional communication with labview, read/write odb, etc, simplest is probably
to write the "mjserver" that talks midas json-rpc over plain tcp, without all the http/https
gunk you need to go through mhttpd.
K.O. |
21 Mar 2025, Stefan Ritt, Forum, LabView-Midas interface
|
> Hello,
>
> Does anyone have experience with writing a MIDAS frontends to communicate with a device that operates using LabView (e.g. superconducting magnets, cryostats etc.). Any information or experience regarding this would be highly appreciated.
>
> thanks,
> Zaher
We do have a superconducting magnet from Cryogenic, UK, which comes with a LabView control program on a Windows PC. I did the only reasonable with this: trash it in the waste basket. Do NOT use Labveiw for anything which should run more than 24h in a row. Too many bad experiences with LabView control programs
for separators at PSI and other devices. Instead of the Windows PC, we use MSCB devices and RasperryPis to communicate with the power supply directly, which has been proven to be much more stable (running for years without crashes). I'm happy to share our code with you.
Stefan |
21 Mar 2025, Konstantin Olchanski, Forum, LabView-Midas interface
|
> > Hello,
> >
> > Does anyone have experience with writing a MIDAS frontends to communicate with a device that operates using LabView (e.g. superconducting magnets, cryostats etc.). Any information or experience regarding this would be highly appreciated.
> >
> > thanks,
> > Zaher
>
> We do have a superconducting magnet from Cryogenic, UK, which comes with a LabView control program on a Windows PC. I did the only reasonable with this: trash it in the waste basket. Do NOT use Labveiw for anything which should run more than 24h in a row. Too many bad experiences with LabView control programs
> for separators at PSI and other devices. Instead of the Windows PC, we use MSCB devices and RasperryPis to communicate with the power supply directly, which has been proven to be much more stable (running for years without crashes). I'm happy to share our code with you.
>
Our parallel experience with the CERN ALPHA anti-hydrogen experiment: they have developed a whole labview empire
to control the cryogenics, the magnets, the positron source, the anti-proton trap, anti-hydrogen trap, etc.
At some point there was a wall of monitors in the counting room - each labview computer controlled one or two things -
so there is very many computers and each had to have a monitor (and mouse and keyboard).
All the data from this labview empire is logged to MIDAS history via felabview and feGEM, and they use
the MIDAS history to look and monitor almost everything. Control is done via Labview and Labview
based FPGA sequencers (National Instruments PXI hardware, $$$$$).
This works reasonably well to publish several papers in Nature.
But not 100%:
1) difficulties with labview source control (cannot be trivially managed by git, I guess)
2) unending fight against Microsoft and CERN IT trying to reboot the computers at the wrong time
3) more recently, forced Microsoft updates require trashing perfectly good machines and buy new ones
At TRIUMF there is very little Labview. All experiments use MIDAS and EPICS for most things.
Based on this experience, I agree with Stefan, today's sweet spot is RaspberryPi machines with USB attached
gizmos to control stuff. On the software side, drive the mess with MIDAS and custom web pages.
K.O. |
23 Mar 2025, Zaher Salman, Forum, LabView-Midas interface
|
Thanks Stefan, I would be very interested to see your code. At moment we have magnets and cryostats (3He and dilution) being delivered with Labview.
> > Hello,
> >
> > Does anyone have experience with writing a MIDAS frontends to communicate with a device that operates using LabView (e.g. superconducting magnets, cryostats etc.). Any information or experience regarding this would be highly appreciated.
> >
> > thanks,
> > Zaher
>
> We do have a superconducting magnet from Cryogenic, UK, which comes with a LabView control program on a Windows PC. I did the only reasonable with this: trash it in the waste basket. Do NOT use Labveiw for anything which should run more than 24h in a row. Too many bad experiences with LabView control programs
> for separators at PSI and other devices. Instead of the Windows PC, we use MSCB devices and RasperryPis to communicate with the power supply directly, which has been proven to be much more stable (running for years without crashes). I'm happy to share our code with you.
>
> Stefan |
12 Dec 2024, Stefan Ritt, Suggestion, New alarm sound flag to be tested
|
We had the case in MEG that some alarms were actually just warnings, not very severe. This happens for example if we calibrate our detector
once every other day and modify the hardware which actually triggers the alarm for about an hour or so.
The problem with this is now that the alarm sounds every few minutes, and people get annoyed during that hour. They turn down the volume
of their speakers, or even disable the alarm sound. If the detector gets back into the default mode again, they might forget to re-enable the
alarm, which causes some risk.
Turning down the volume is also not good, since during that hour we could have a "real" alarm on which people have to react quickly in order
not destroy the detector.
The art is now to configure the alarm system in a way that "normal" changes do not annoy people or cover up really severe alarms. After long
discussions we came to following conclusion: We need a special class of alarm (let's call it 'warning') which does not annoy people. The
warning should be visible on the screen, but not ring the alarm bell.
While we have different alarm classes in midas, which let us customize the frequency of alarms and the screen colors, all alarms or warnings
ring the alarm sound right now. This can be changed in the browser under "Config/Alarm sound" but that switch affects ALL alarms, which is
not what we want.
The idea we came up with was to add a flag "Alarm sound" to the alarm classes. For the 'warning' we can then turn off the alarm sound, so
only the banner is shown on top of the screen, and the spoken message is generated every 15 mins to remind people, but not to annoy them.
I added this "Alarm sound" flag in the branch feature/alarm_sound so everybody can test it. The downside is that all "Alarm/Classs/xxx" need
to be modified to add this flag. While the new code will add this flag automatically (with a default value of 'true'), the size of the alarm class
record changes by four bytes (one bool). Therefore, all running midas programs will complain about the changed size, until they get
recompiled.
Therefore, to test the new feature, you have to checkout the branch and re-compile all midas programs you use, otherwise you will get errors
like
Fixing ODB "/Alarms/Classes/Alarm" struct size mismatch (expected 352, odb size 348)
I will keep the branch for a few days for people to try it out and report any issue, and later merge it to develop.
Stefan |
19 Dec 2024, Stefan Ritt, Suggestion, New alarm count added
|
Another modification has been done to the alarm system.
We have often cases where an alarm is triggered on some readout noise. Like an analog voltage just over the alarm threshold for a very short period of time, triggered sometimes from environmental
electromagnetic effects like turning on the light.
To mitigate this problem, an "alarm trigger count" has been implemented. Every alarm has now a variable "Trigger count required". If this value is zero (the default), the alarm system works as before. If this
value is nowever set to a non-zero value N, the alarm limit has to be met on N consecutive periods in order to trigger the alarm. Each alarm has a "Check interval" which determines the period of the alarm
checking. If one has for example:
Check interval = 10
Trigger count required = 3
then the alarm condition has to be met for 3 consecutive periods of 10 seconds each, or for 30 seconds in total.
The modification has been merged into the develop branch, and people have to be aware that the alarm structures in the ODB changed. The current code tries to fix this automatically, but it is important
that ALL MIDAS CLIENTS GET RE-COMPILED after the new code is applied. Otherwise we could have "new" clients expecting the new ODB structure, and some "old" clients expecting the old structure,
then both types of clients would fight against each other and change the ODB structure every few seconds, leading to tons of error messages. So if you pull the current develop branch, make sure to re-
compile ALL midas clients.
/Stefan |
20 Mar 2025, Konstantin Olchanski, Suggestion, BINARY INCOMPATIBLE CHANGE: New alarm count added
|
> ALL MIDAS CLIENTS GET RE-COMPILED after the new code is applied.
Usually we expect that it is "safe" to update to the latest version of MIDAS.
In the sense that we do not have to track down every single frontend
and rebuild it. We have several experiments where tracking down the source code
and rebuilding a frontend takes more than 5 seconds. In many cases these are
10 year old executables that worked just fine through many updates of MIDAS
without having to rebuild them.
So any binary incompatible change is best avoided and must be clearly announced.
The present binary-incompatible change is this commit:
https://bitbucket.org/tmidas/midas/commits/5899f1657ba31121c9f420a824b3c6c13b173988
I tagged the last commit before this change as: midas-2024-12-a
K.O. |
21 Mar 2025, Stefan Ritt, Suggestion, BINARY INCOMPATIBLE CHANGE: New alarm count added
|
> > ALL MIDAS CLIENTS GET RE-COMPILED after the new code is applied.
>
> Usually we expect that it is "safe" to update to the latest version of MIDAS.
>
> In the sense that we do not have to track down every single frontend
> and rebuild it. We have several experiments where tracking down the source code
> and rebuilding a frontend takes more than 5 seconds. In many cases these are
> 10 year old executables that worked just fine through many updates of MIDAS
> without having to rebuild them.
>
> So any binary incompatible change is best avoided and must be clearly announced.
>
> The present binary-incompatible change is this commit:
> https://bitbucket.org/tmidas/midas/commits/5899f1657ba31121c9f420a824b3c6c13b173988
>
> I tagged the last commit before this change as: midas-2024-12-a
Here are my replies:
- this is not a binary incompatibility, but a incompatibility of the /Alarm record which got two more variables. Old
frontends will complain during their structure check and remove the two variables, new frontend will then complain as
well and add the two variables. This will go in circles, that why all frontends need to be recompiled if the new code is
used
- I did clearly announce this change in the forum. Is there another location where I should communicate that?
- The extension is not there "just for fun" but needed by several experiments which experience spurious alarms
triggered by some noisy signals. Requiring an value to be above a limit for a certain minimum time fixes many issues in
many experiments here at PSI, this is why it has been implemented, even if it causes work for everybody with re-
compilation.
- If there are frontend programs which have not been re-compiled for ten years, I think it is very unlikely that these
experiments need the latest cutting edge version of midas. The can safely stick to your tag midas-2024-12-a and not
experience the issue of different /Alarm trees.
Stefan |
21 Mar 2025, Konstantin Olchanski, Suggestion, BINARY INCOMPATIBLE CHANGE: New alarm count added
|
> > > ALL MIDAS CLIENTS GET RE-COMPILED after the new code is applied.
>
> - I did clearly announce this change in the forum.
>
I missed the announcement and I was surprised to discover this change.
This is why I changed the title from "useful improvement" to "ACHTUNG!!!"
> - The extension is not there "just for fun" but needed by several experiments which experience spurious alarms
> triggered by some noisy signals.
Agreed, a useful improvement.
To prevent this kind of trouble in the future, I think I should finish my crusade against db_get_record().
> - If there are frontend programs which have not been re-compiled for ten years, I think it is very unlikely that these
> experiments need the latest cutting edge version of midas. The can safely stick to your tag midas-2024-12-a and not
> experience the issue of different /Alarm trees.
Yes, now that I added the tag, posted a more visible notice and people who have to run frontends build against older midas
(because reasons) can breathe out.
Also please note that "Konstantin != all experiments at TRIUMF".
K.O. |
20 Mar 2025, Konstantin Olchanski, Bug Fix, bitbucket builds fixed
|
bitbucket automatic builds were broken after mfe.cxx started printing some additional messages added in commit
https://bitbucket.org/tmidas/midas/commits/0ae08cd3b96ebd8e4f57bfe00dd45527d82d7a38
this is now fixed. to check if your changes will break automatic builds, before final push, please do:
make clean
make mini -j
make cmake -j
make test
K.O. |
21 Mar 2025, Stefan Ritt, Bug Fix, bitbucket builds fixed
|
> bitbucket automatic builds were broken after mfe.cxx started printing some additional messages added in commit
> https://bitbucket.org/tmidas/midas/commits/0ae08cd3b96ebd8e4f57bfe00dd45527d82d7a38
>
> this is now fixed. to check if your changes will break automatic builds, before final push, please do:
>
> make clean
> make mini -j
> make cmake -j
> make test
Unfortunately we will break the automatic build each time a program outputs one different character, which even might happen if we add a line of code and
a cm_msg() gets produced with a different line number. Is there a standard way to update testexpt.example (like "make testexpt" or so). Should be trigger
the update of testexpt.example before each commit via a hook?
Stefan |
21 Mar 2025, Konstantin Olchanski, Bug Fix, bitbucket builds fixed
|
> > bitbucket automatic builds
>
> Unfortunately we will break the automatic build each time a program outputs one different character, which even might happen if we add a line of code and
> a cm_msg() gets produced with a different line number. Is there a standard way to update testexpt.example (like "make testexpt" or so). Should be trigger
> the update of testexpt.example before each commit via a hook?
>
Actually line numbers are not logged by messages printed from "make test", so moving code around does not break the test.
Changing what programs output does break the test and this is intentional - somebody must look at confirm
that program output was changed on purpose or because a bug was introduced (or fixed).
Most "make test" things work this way - run programs, compare output to what is expected. Discrepancies are flagged for human examination.
K.O. |
07 Feb 2025, Konstantin Olchanski, Info, switch midas to next c++
|
to continue where we left off in 2019,
https://daq00.triumf.ca/elog-midas/Midas/1520
time to choose the next c++!
snapshot of 2019:
- Linux RHEL/SL/CentOS6 - gcc 4.4.7, no C++11.
- Linux RHEL/SL/CentOS7 - gcc 4.8.5, full C++11, no C++14, no C++17
- Ubuntu 18.04.2 LTS - gcc 7.3.0, full C++11, full C++14, "experimental" C++17.
- MacOS 10.13 - llvm 10.0.0 (clang-1000.11.45.5), full C++11, full C++14, full C++17
the world moved on:
- el6/SL6 is gone
- el7/CentOS-7 is out the door, only two experiments on my plate (EMMA and ALPHA-g)
- el8 was a still born child of RedHat
- el9 - gcc 11.5 with 12, 13, and 14 available.
- el10 - gcc 14.2
- U-18 - gcc 7.5
- U-20 - gcc 9.4 default, 10.5 available
- U-22 - gcc 11.4 default, 12.3 available
- U-24 - gcc 13.3 default, 14.2 available
- MacOS 15.2 - llvm/clang 16
Next we read C++ level support:
(see here for GCC C++ support: https://gcc.gnu.org/projects/cxx-status.html)
(see here for LLVM clang c++ support: https://clang.llvm.org/cxx_status.html)
(see here for GLIBC c++ support: https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html)
gcc:
4.4.7 - no C++11
4.8.5 - full C++11, no C++14, no C++17
7.3.0 - full C++11, full C++14, "experimental" C++17.
7.5.0 - c++17 and older
9.4.0 - c++17 and older
10.5 - no c++26, no c++23, mostly c++20, c++17 and older
11.4 - no c++26, no c++23, full c++20 and older
12.3 - no c++26, mostly c++23, full c++20 and older
13.3 - no c++26, mostly c++23, full c++20 and older
14.2 - limited c++26, mostly c++23, full c++20 and older
clang:
16 - no c++26, mostly c++23, mostly c++20, full c++17 and older
I think our preference is c++23, the number of useful improvements is quite big.
This choice will limit us to:
- el9 or older
- U-22 or older
- current MacOS 15.2 with Xcode 16.
It looks like gcc and llvm support for c++23 is only partial, so obviously we will use a subset of c++23
supported by both.
Next step is to try to build midas with c++23 on el9 and U-22,24 and see what happens.
K.O. |
20 Mar 2025, Konstantin Olchanski, Info, switch midas to next c++
|
> time to choose the next c++!
Ununtu-24.04, MIDAS builds with -std=c++23 without errors or any additional warnings. (but does it work? "make
test" is ok).
K.O. |
20 Mar 2025, Konstantin Olchanski, Info, make coverage
|
Some time ago Ben Smith added test coverage reports using GCC -fprofile-arcs -
ftest-coverage and lcov.
It reports code coverage after running "make test". Reported code coverage is
surprisingly high for the very little code executed by "make test" - create ODB,
start a frontend, start run, stop run, check that mlogger created data files
and history files.
(Of course coverage can never reach 100%, some obscure branches are unreachable
without using an automated fuzzer, and some error paths are unreachable without
also using a system call fault injector).
Simplest way to generate a coverage report:
make clean
make cmake YES_COVERAGE=1
make coverage
open cov_html/index.html
K.O. |
11 Mar 2025, Federico Rezzonico, Bug Report, python hist_get_events not returning events, but javascript does
|
After starting midas (mhttpd &, and mlogger -D) and running the `fetest` frontend I went into the midas/python/examples directory and ran basic_hist_script.py, and, even though I could see the 'pytest' program in the Programs page,
Valid events are:
Enter event name:
was printed out, which signified that no events were found. No errors were displayed.
Instead, when trying to do the same in javascript (using mjsonrpc_send_request( mjsonrpc_make_request("hs_get_events")).then(console.log)), I was able to get the expected events.
The History page also displayed the expected data and the plots worked correctly.
Device info: Chip: Apple M1 Pro, OS: Sequoia (15.3)
MIDAS version: bitbucket commit 84c7ef7
Python version: 3.13.2 |
11 Mar 2025, Ben Smith, Bug Report, python hist_get_events not returning events, but javascript does
|
> Valid events are:
> Enter event name:
>
> was printed out, which signified that no events were found. No errors were displayed.
I can't reproduce this. I made a brand new experiment, started mlogger/mhttpd/fetest, then ran the same program. I get:
```
$ python basic_hist_script.py
Valid events are:
* Run transitions
* test_slow/data
Enter event name:
```
Are you sure you ran the python program after running mlogger and not before? Can you try again after restarting mlogger? And can you verify that your python is connecting to the correct experiment if you have multiple experiments defined?
I tested with python 3.12.8 and 3.13.1, and am on MacOS 14.5, but I can't imagine those differences matter.
The python interface is a trivial wrapper around the C++ function, so the only python-specific thing that would result in an empty list is extracting an integer from a ctypes reference. If that's broken in your version then I don't think any of the midas python code would be working. |
14 Mar 2025, Konstantin Olchanski, Bug Report, python hist_get_events not returning events, but javascript does
|
> After starting midas (mhttpd &, and mlogger -D) and running the `fetest` frontend I went into the midas/python/examples directory and ran basic_hist_script.py, and, even though I could see the 'pytest' program in the Programs page,
>
> Valid events are:
> Enter event name:
>
> was printed out, which signified that no events were found. No errors were displayed.
To check that MIDAS itself is built correctly, you can try "make test", this will create a sample experiment,
run fetest, start, stop a run and check that data file and history file is created with correct history events.
If "make test" fails, I can help debug it.
In your experiment, you can check that history files are created correctly:
1) "mhist -l" should show all available events
2) "mhdump -L *.hst" should show all events in the .hst history files
3) if you have the newer mhf*.dat files, you can "more mhf_1449770978_20151210_hv.dat" to see what data is inside
If all of that works as expected, there must be a problem with the python side and we will have to figure
out how to reproduce it.
This reminds me, "make test" does not test any of the python code, it should be added (and python should be added
to the bitbucket builds).
K.O. |
16 Mar 2025, Federico Rezzonico, Bug Report, python hist_get_events not returning events, but javascript does
|
> After starting midas (mhttpd &, and mlogger -D) and running the `fetest` frontend I went into the midas/python/examples directory and ran basic_hist_script.py, and, even though I could see the 'pytest' program in the Programs page,
>
> Valid events are:
> Enter event name:
>
> was printed out, which signified that no events were found. No errors were displayed.
>
> Instead, when trying to do the same in javascript (using mjsonrpc_send_request( mjsonrpc_make_request("hs_get_events")).then(console.log)), I was able to get the expected events.
>
> The History page also displayed the expected data and the plots worked correctly.
>
> Device info: Chip: Apple M1 Pro, OS: Sequoia (15.3)
>
> MIDAS version: bitbucket commit 84c7ef7
>
> Python version: 3.13.2
I tested the command this morning and it worked. The most likely cause of the errors was that I was on a beta version of macOS: Switching beta updates off fixed the issue. Thanks for the help! |
19 Mar 2025, Konstantin Olchanski, Bug Report, python hist_get_events not returning events, but javascript does
|
> beta version of macOS: Switching beta updates off fixed the issue.
I would be very surprised if that was the problem.
Bigger concern is that it fails without producing any useful error message.
Latest MacOS makes it extremely difficult to debug this kind of stuff, there
is several hoops to jump through to enable core dumps and to allow lldb
to attach and debug running programs.
K.O. |
19 Mar 2025, Konstantin Olchanski, Forum, MidasWiki special page access now restricted to logged in users
|
We have a problem with over aggressive AI bots. They are scanning everything and
are putting a crazy load on the mediawiki mysql database. This makes all out
wikis very slow and unresponsive, which is a big problem.
These AI scanners do not seem to know what they are doing and are trying to load
all the special pages, like "what links here" and "recent changes" and complete
revision histories for each page. I am not impressed. Old school google and bing
scanner bots are much more respectful and do not cause problems.
AI bots are also scanning this elog, and to Stefan's credit elogd is holding the
load just fine.
To reduce load on the MidasWiki mysql database, I now restricted access to most
special pages to logged in users. It seems to have helped.
If this causes big difficulty or if you have suggestion on better ways to deal
with aggressive AI scanner bots, please speak up.
One alternative is to put MidasWiki behind a generic login page with a well
known password. Downside is it will block google and bing search engines, too.
K.O. |
17 Mar 2025, Federico Rezzonico, Bug Report, python hist_get_recent_data returns no historical data
|
Setup:
setting up midas, starting mhttpd and mlogger and running fetest.
The History page and the javascript mjsonrpc client are both able to fetch historical data for test_slow/data. Javascript code used is included here:
mjsonrpc_call(
"hs_read_arraybuffer",
{
start_time: Math.floor((new Date()).getTime() /1000) - 1000,
end_time: Math.floor((new Date()).getTime() /1000),
events: ["test_slow/data"],
tags: ["data"],
index: [0],
},
"arraybuffer"
).then(console.log)
However, the python client does not find any valid events:
Setup:
An exptab is created and the environment variables MIDAS_EXPTAB and MIDAS_EXPT_NAME and MIDASSYS are set (together with the correct PATH)
Running /midas/python/examples/basic_hist_script.py and typing in data:
Valid events are:
* Run transitions
* rrandom/SLOW
* test_slow/data
Enter event name: test_slow/data
Valid tags for test_slow/data are:
* data
Enter tag name: data
Event/tag test_slow/data/data has 1 elements
How many hours: 1
Interval in seconds: 1 # other values were also tested, without success
0 entries found
We expect entries to be found, however do not.
Tested setups:
Macbook Pro Sequoia 15.3 with Python 3.13.2, ROOT latest, midas bitbucket commit 84c7ef7
Windows 11 with Python 3.11, ROOT latest, midas latest commit (development branch) |
17 Mar 2025, Ben Smith, Bug Report, python hist_get_recent_data returns no historical data
|
Unfortunately I again cannot reproduce this:
$ python ~/DAQ/midas_latest/python/examples/basic_hist_script.py
Valid events are:
* Run transitions
* test_slow/data
Enter event name: test_slow/data
Valid tags for test_slow/data are:
* data
Enter tag name: data
Event/tag test_slow/data/data has 1 elements
How many hours: 1
Interval in seconds: 1
78 entries found
2025/03/17 17:00:56 => 98.097391
2025/03/17 17:00:57 => 98.982151
2025/03/17 17:00:58 => 99.589187
2025/03/17 17:00:59 => 99.926821
2025/03/17 17:01:00 => 99.989878
2025/03/17 17:01:01 => 99.778216
2025/03/17 17:01:02 => 99.292485
.......
I want to narrow down whether the issue is in the basic_hist_script.py or the lower-level code. So there are a few steps of debugging to do.
1) Run code directly in the python interpreter:
Can you run the following and send the output please?
```
import midas.client
c = midas.client.MidasClient("history_test")
data = c.hist_get_recent_data(1,1,"test_slow/data","data")
print(f"event_name='{data[0]['event_name']}', tag_name='{data[0]['tag_name']}', num_entries={data[0]['num_entries']}, status={data[0]['status']}, arrlen={len(data[0]['values'])}")
```
For me, I get:
event_name='test_slow/data', tag_name='data', num_entries=441, status=1, arrlen=441
2) If things look sensible for you (status=1, non-zero num_entries), then the problem is in the basic_hist_script.py. Can you add the same print() statement in basic_hist_script.py immediately after the call to hist_get_recent_data(), then run that script again and send the output of that?
3) Debug the python/C conversions.
In midas/client.py add the following line to hist_get_data() immediately before the call to self.lib.c_hs_read():
```
print(f"c_start_time={c_start_time.value}, c_end_time={c_end_time.value}, c_interval={c_interval.value}, c_event_name={c_event_name.value}, c_tag_name={c_tag_name.value}")
```
Then run the following and send the output:
```
import midas.client
c = midas.client.MidasClient("history_test")
data = c.hist_get_recent_data(1,1,"test_slow/data","data")
```
For me, I get:
c_start_time=1742254428, c_end_time=1742258028, c_interval=1, c_event_name=b'test_slow/data', c_tag_name=b'data'
I want to check that the UNIX timestamps match what you expect for your server, and that nothing weird is going on with the python/C string conversions.
Thanks,
Ben |
18 Mar 2025, Federico Rezzonico, Bug Report, python hist_get_recent_data returns no historical data
|
> Unfortunately I again cannot reproduce this:
>
> $ python ~/DAQ/midas_latest/python/examples/basic_hist_script.py
> Valid events are:
> * Run transitions
> * test_slow/data
> Enter event name: test_slow/data
> Valid tags for test_slow/data are:
> * data
> Enter tag name: data
> Event/tag test_slow/data/data has 1 elements
> How many hours: 1
> Interval in seconds: 1
> 78 entries found
> 2025/03/17 17:00:56 => 98.097391
> 2025/03/17 17:00:57 => 98.982151
> 2025/03/17 17:00:58 => 99.589187
> 2025/03/17 17:00:59 => 99.926821
> 2025/03/17 17:01:00 => 99.989878
> 2025/03/17 17:01:01 => 99.778216
> 2025/03/17 17:01:02 => 99.292485
> .......
>
>
> I want to narrow down whether the issue is in the basic_hist_script.py or the lower-level code. So there are a few steps of debugging to do.
>
>
>
> 1) Run code directly in the python interpreter:
>
> Can you run the following and send the output please?
>
> ```
> import midas.client
> c = midas.client.MidasClient("history_test")
> data = c.hist_get_recent_data(1,1,"test_slow/data","data")
> print(f"event_name='{data[0]['event_name']}', tag_name='{data[0]['tag_name']}', num_entries={data[0]['num_entries']}, status={data[0]['status']}, arrlen={len(data[0]['values'])}")
> ```
>
> For me, I get:
> event_name='test_slow/data', tag_name='data', num_entries=441, status=1, arrlen=441
>
>
>
> 2) If things look sensible for you (status=1, non-zero num_entries), then the problem is in the basic_hist_script.py. Can you add the same print() statement in basic_hist_script.py immediately after the call to hist_get_recent_data(), then run that script again and send the output of that?
>
>
>
> 3) Debug the python/C conversions.
>
> In midas/client.py add the following line to hist_get_data() immediately before the call to self.lib.c_hs_read():
>
> ```
> print(f"c_start_time={c_start_time.value}, c_end_time={c_end_time.value}, c_interval={c_interval.value}, c_event_name={c_event_name.value}, c_tag_name={c_tag_name.value}")
> ```
>
> Then run the following and send the output:
>
> ```
> import midas.client
> c = midas.client.MidasClient("history_test")
> data = c.hist_get_recent_data(1,1,"test_slow/data","data")
> ```
>
> For me, I get:
> c_start_time=1742254428, c_end_time=1742258028, c_interval=1, c_event_name=b'test_slow/data', c_tag_name=b'data'
>
> I want to check that the UNIX timestamps match what you expect for your server, and that nothing weird is going on with the python/C string conversions.
>
>
> Thanks,
> Ben
Hi, thank you for the support!
Running the commands on the Macbook pro leads to
1)
event_name='test_slow/data', tag_name='data', num_entries=0, status=1, arrlen=0
2)
The number of entries is zero
3)
I get
c_start_time=1742275653, c_end_time=1742279253, c_interval=1, c_event_name=b'test_slow/data', c_tag_name=b'data'
However right after running these commands I removed a .SHM_HOST.TXT file
(due to me working both at home and at PSI, my computer hostname changes when I switch the network, so I remove .SHM_HOST.TXT to be able to run experiments)
and reran the code, and it suddenly worked! This is good, but I do not know what fixed it... I had done more extensive tests yesterday and also had to delete .SHM_HOST.TXT multiple times, to no avail.
Do you have any ideas as to what could be happening? Similarly to my previous bug report, which was fixed by updating macOS to a more stable version, could this have been due to an automatic update?
If the problem still persists on the Windows machine I will post an update. |
18 Mar 2025, Konstantin Olchanski, Bug Report, python hist_get_recent_data returns no historical data
|
>
> However right after running these commands I removed a .SHM_HOST.TXT file
>
Instead of deleting .SHM_HOSTS.TXT, please create it as an empty file. I thought the documentation is clear about it?
Also we recommend installing MIDAS to $HOME/packages/midas. There is a number of problems if installed at top level.
If you want to be compliant with the Linux LFS, /opt/midas is also a good place.
>
> ... and it suddenly worked!
>
We still did not establish if mhist, mhdump and the other commands I sent you work correctly,
to confirm MIDAS is creating correct history files. (before you try to read them with python).
Also we did not establish that you have correct paths setup in ODB /Logger/History.
Many things can go wrong.
Next time python history malfunctions, please do all those other things and report to us. Thanks!
K.O. |
28 Feb 2025, Zaher Salman, Info, Syntax validation in sequencer
|
Hello,
I've implemented a very basic syntax validation in the sequencer GUI. Click the validation button to check the syntax in the current tab.
Please note that this does only a simple syntax validation, the correctness of the logic is still on you :) |
05 Feb 2025, Andreas Suter, Forum, Transition from mana -> manalyzer
|
Hi,
we are planning to migrate from mana to manalyzer. I started to have a look into it and realized that I have some lose ends.
Is there a clear migration docu somewhere?
Currently I understand it the following way (which might be wrong):
The class TARunObject is used to write analyzer modules which are registered by TAFactory. I hope this is right?
However, in mana there is an analyzer implemented by the user which binds the modules and has additional routines:
analyzer_init(), analyzer_exit(), analyzer_loop()
ana_begin_of_run(), ana_end_of_run(), ana_pause_run(), ana_resume_run()
which we are using.
This part I somehow miss in manalyzer, most probably due to lack of understanding, and missing documentation.
Could somebody please give me a boost? |
05 Feb 2025, Andrea Capra, Forum, Transition from mana -> manalyzer
|
Hi Andreas,
please find in elog:2938/1 a short introduction that I wrote sometime ago.
I'm glad to offer additional support, if needed.
> Hi,
>
> we are planning to migrate from mana to manalyzer. I started to have a look into it and realized that I have some lose ends.
> Is there a clear migration docu somewhere?
>
> Currently I understand it the following way (which might be wrong):
> The class TARunObject is used to write analyzer modules which are registered by TAFactory. I hope this is right?
>
> However, in mana there is an analyzer implemented by the user which binds the modules and has additional routines:
> analyzer_init(), analyzer_exit(), analyzer_loop()
> ana_begin_of_run(), ana_end_of_run(), ana_pause_run(), ana_resume_run()
> which we are using.
>
> This part I somehow miss in manalyzer, most probably due to lack of understanding, and missing documentation.
>
> Could somebody please give me a boost? |
06 Feb 2025, Konstantin Olchanski, Forum, Transition from mana -> manalyzer
|
> Could somebody please give me a boost?
no need to shout into the void, it is pretty easy to identify the author of manalyzer and ask me directly.
> we are planning to migrate from mana to manalyzer. I started to have a look into it and realized that I have some lose ends.
> Is there a clear migration docu somewhere?
README.md and examples in the manalyzer git repository.
If something is missing, or unclear, please ask.
> Currently I understand it the following way (which might be wrong):
> The class TARunObject is used to write analyzer modules which are registered by TAFactory. I hope this is right?
Please read the README file. It explains what is going on there.
Design of manalyzer had 2 main goals:
a) lifetime of all c++ objects (and ROOT objects) is well defined (to fix a design defect in rootana)
b) event flow and data flow are documentable (problem in mfe.c frontends, etc)
> However, in mana there is an analyzer implemented by the user which binds the modules and has additional routines:
> analyzer_init(), analyzer_exit(), analyzer_loop()
> ana_begin_of_run(), ana_end_of_run(), ana_pause_run(), ana_resume_run()
> which we are using.
I have never used the mana analyzer, I wrote the c++ rootana analyzer very early on (first commit in 2006).
But the basic steps should all be there for you:
- initialization (create histograms, open files) can be done in the module constructor or in BeginRun()
- finalization (fit histograms, close files) should be done in EndRun()
- event processing (obviously) in Analyze()
- pause run, resume run and switch to next subrun file have corresponding methods
- all the "flow" and multithreading stuff you can ignore to first order.
To start the migration, I recommend you take manalyzer_example_root.cxx and start stuffing it with your code.
If you run into any problems, I am happy to help with them. Ask here or contact me directly.
> This part I somehow miss in manalyzer, most probably due to lack of understanding, and missing documentation.
True, I wrote a migration guide for the frontend mfe.c to c++ tmfe, because we do this migration
quite often. But I never wrote a migration guide from mana.c analyzer, because we never did such
migration. Most experiments at TRIUMF are post-2006 and use rootana in it's different incarnations.
P.S. I designed the C++ TMFe frontend after manalyzer and I think it came out quite better, I especially
value the design input from Stefan, Thomas, Pierre, Joseph and Ben.
P.P.S. Be free to ignore all this manalyzer business and write your own analyzer based
on the midasio library:
int main()
{
TMReaderInteraface* f = TMNewReader(file.mid.gz");
while (1) {
TMEvent* e = TMReadEvent(f);
dwim(e);
delete e;
}
delete f;
}
For online processing I use the TMFe class, it has enough bits to be a frontend and an analyzer,
or you can use the older TMidasOnline from rootana.
Access to ODB is via the mvodb library, which is new in midas, but has been part of rootana
and my frontend toolkit since at least 2011 or earlier, inspired by Peter Green's even
older "myload" ODB access library.
K.O. |
06 Feb 2025, Konstantin Olchanski, Forum, Transition from mana -> manalyzer
|
> > Is there a clear migration docu somewhere?
I can also give you links to the alpha-g analyzer (very complex) and the DarkLight trigger TDC analyzer (very simple),
there is also analyzer examples of in-between complexity.
K.O. |
18 Jan 2025, Pavel Murat, Forum, mjsonrpc: how to increase the max allowed length of the callback response ?
|
Dear MIDAS experts,
I'm using MIDAS javascript interface (mjsonrpc) to communicate with a frontend from a custom web page
and observe that the if the frontend's response exceeds certain number of bytes, it is always truncated.
MIDAS C/C++ RPC interface allows users to specify the max response length :
https://daq00.triumf.ca/MidasWiki/index.php/Remote_Procedure_Calls_(RPC)#C++_2
How would one do the same from with mjsonrpc ?
-- many thanks, regards, Pasha |
20 Jan 2025, Ben Smith, Forum, mjsonrpc: how to increase the max allowed length of the callback response ?
|
> I'm using MIDAS javascript interface (mjsonrpc) to communicate with a frontend from a custom web page
> and observe that the if the frontend's response exceeds certain number of bytes, it is always truncated.
>
> MIDAS C/C++ RPC interface allows users to specify the max response length :
>
> https://daq00.triumf.ca/MidasWiki/index.php/Remote_Procedure_Calls_(RPC)#C++_2
>
> How would one do the same from with mjsonrpc ?
I just documented the max_reply_length (javascript) and max_len (python) parameters on that page. Both are optional and default to 1024 bytes.
I also added a link to the full mjsonrpc schema https://daq00.triumf.ca/MidasWiki/index.php/Mjsonrpc#Schema_(List_of_all_RPC_methods) . You can also find the auto-generated schema on any midas installation by going to the "Help" webpage served by mhttpd and clicking the "JSON-RPC schema" > "text table format" link. |
29 Dec 2024, Pavel Murat, Forum, time ordering of run transition calls to TMFeEquipment things
|
Dear MIDAS experts,
I have a question about "tmfe approach" to implementing MIDAS frontends. If I read the code correctly,
within this approach it is the TMFeEquipment things, not the TMFrontend's themselves,
which handle the run transitions - the TMFrontend class
https://bitbucket.org/tmidas/midas/src/423082fb67c7711813fcda61f7cd03784c398f49/include/tmfe.h#lines-306:378
simply doesn't have methods to handle those directly.
So how does a user control the sequence in which TMFeEquipment::HandleBeginRun functions of different
TMFeEquipment pieces are called at begin run? - there are two cases to consider: TMFeEquipment things
defined by the same TMFrontend and by different TMFrontend's.
Many thanks and happy holidays to everyone!
-- regards, Pasha
|
01 Jan 2025, Konstantin Olchanski, Forum, time ordering of run transition calls to TMFeEquipment things
|
> I have a question about "tmfe approach" to implementing MIDAS frontends. If I read the code correctly,
> within this approach it is the TMFeEquipment things, not the TMFrontend's themselves,
> which handle the run transitions - the TMFrontend class
that's correct and it is documented so in https://bitbucket.org/tmidas/midas/src/develop/tmfe.md
> So how does a user control the sequence in which TMFeEquipment::HandleBeginRun functions of different
> TMFeEquipment pieces are called at begin run? - there are two cases to consider: TMFeEquipment things
> defined by the same TMFrontend and by different TMFrontend's.
I am not sure what you are trying to do. It is always easier to suggest a solution to a specific problem.
But I will try to answer anyway:
1) "time ordering of run transitions" - of course midas transitions are ordered by transition sequence numbers
and the tmfe class provides methods to control this. ditto for the mfe.cxx frontends.
2) for one TMFrontend, the order of calling HandleBeginRun() is the order in which equipments were added to the
equipment using FeAddEquipment(). HandleEndRun() is called in reverse order. (I better check this).
3) to have multiple TMFrontends in one program would be unusual (mfe.cxx frontends completely do not support
this), but should work. Everything was coded to support this, but it was never tested in practice because we
cannot invent any useful use-case for it. HandleBeginRun() handlers are likely to be called in the frontends are
created. (I could check this and confirm it works, as long as you have a valid use-case for this configuration).
4) Frontend X has EquipmentA and EquipmentB, you want EqA::HandleBeginRun() to be called at run transition 200
and EqB::HandleBeginRun() to be called at run transition 400.
This is not directly supported by mfe.cxx frontends (the begin_run() handler is a global function) and I did not
directly implement it in the TMFE frontend.
But I think this would be a useful improvement. I will look into this.
Likely I will add per-equipment data members fEqConfBeginRunSeqNo, fEqConfEndRunSeqno, etc. Value 0 would
unregister the corresponding run transition handler. This would cleanup the code quite a bit, a bunch
of RegisterTranstionXXX functions could go away.
K.O. |
02 Jan 2025, Pavel Murat, Forum, time ordering of run transition calls to TMFeEquipment things
|
Hi K.O., your clarification is much appreciated!
"
> I am not sure what you are trying to do. It is always easier to suggest a solution to a specific problem.
I think, I owe you an explanation :) :
Consider ~ 40 nodes with two FPGAs (PCIE cards) per node, talking to the detector hardware.
One of those FPGAs, in addition to reading the data, performs the global timing synchronization.
The high-bandwidth data readout is not controlled by MIDAS, so all frontends perform only 'slow control'-type functions.
In MIDAS language, an FPGA implements two different units of slow control equipment:
one - configuring and controlling a single FPGA (equipment type A), and another one - synchronizing
multiple FPGAs (equipment type B). On one of the nodes, unit A and unit B share the FPGA card,
so they better be controlled by the same frontend.
For one, I need to make sure that all type A equipment units, managed by multiple frontends,
are initialized before the [single] type B unit which shares the frontend with the type A unit.
And, of course, the end of a run transition has to be handled in the opposite order - type B unit
shuts down first.
As 'periodic' actions for all registered pieces of equipment are performed in the same loop [thread],
registering the equipment in the needed order - first A, then B - should give a solution - thanks for making that clear.
>
> 1) "time ordering of run transitions" - of course midas transitions are ordered by transition sequence numbers
> and the tmfe class provides methods to control this. ditto for the mfe.cxx frontends.
>
> 2) for one TMFrontend, the order of calling HandleBeginRun() is the order in which equipments were added to the
> equipment using FeAddEquipment(). HandleEndRun() is called in reverse order. (I better check this).
the ordering of the rpc handler calls in tmfe's tr_stop/tr_pause/tr_resume functions is ok.
>
> 3) to have multiple TMFrontends in one program would be unusual (mfe.cxx frontends completely do not support
> this), but should work. Everything was coded to support this, but it was never tested in practice because we
> cannot invent any useful use-case for it. HandleBeginRun() handlers are likely to be called in the frontends are
> created. (I could check this and confirm it works, as long as you have a valid use-case for this configuration).
agreed, I don't think there is a good use case for that, so no need to spend time checking.
>
> 4) Frontend X has EquipmentA and EquipmentB, you want EqA::HandleBeginRun() to be called at run transition 200
> and EqB::HandleBeginRun() to be called at run transition 400.
>
> This is not directly supported by mfe.cxx frontends (the begin_run() handler is a global function) and I did not
> directly implement it in the TMFE frontend.
>
> But I think this would be a useful improvement. I will look into this.
In the simplest case, registering the equipment units in the right order is definitely the answer.
However a single FPGA can perform multiple logically independent tasks and thus represent
multiple logical units of equipment. Those units however are not independent: they share the hardware (FPGA)
and thus do depend on each other. Giving users a full control over the sequence in which those logical units
execute their run transitions is quite likely to be needed, for example, to work around peculiarities of the
custom-made kernel drivers.
>
> Likely I will add per-equipment data members fEqConfBeginRunSeqNo, fEqConfEndRunSeqno, etc. Value 0 would
> unregister the corresponding run transition handler. This would cleanup the code quite a bit, a bunch
> of RegisterTranstionXXX functions could go away.
this also makes sense. -- thanks again, regards, Pasha
>
> K.O. |
05 Jan 2025, Stefan Ritt, Forum, time ordering of run transition calls to TMFeEquipment things
|
Hi Pavel,
have you looked into
cm_set_transition_sequence()
which let's you define the sequence number for every midas client. You give any number between 1 and 1000 (default is 500 for frontends I believe).
The default value of 500 is defined in mfe.cxx:2641 where you have 500 for all four transitions, but it can be overwritten in the frontend_init function via
cm_set_transition_sequence(). Since you have separate values for the start and stop transition, you can get the different sequencing for both transitions as you need. Like set
all type A to 400, type B to 600 for TR_START, and set type A to 600 and type B to 400 for TR_STOP.
Since this works on the midas client level, it should also work for the tmfe.cxx framework. I'm however not sure if you have a similar default of 500 as in mfe.cxx:2641. But K.O.
should know.
Best,
Stefan |
17 Dec 2024, Lukas Gerritzen, Bug Report, [History plots] "Jump to current time" resets x range to 7d
|
To reproduce:
- Open a history plot, click [-] a few times until the x axis shows more than 7 days.
- Scroll to the past (left)
- Click "Jump to current time" (the triangle)
Expected result:
The upper limit of the x axis is at the current time and the lower range is now - whatever range you had before
(>7d)
Actual result:
The upper limit is the current time, the lower limit is now - 7d
(The interval seems unchanged if the range was < 7d before clicking "Jump to current time") |
19 Dec 2024, Stefan Ritt, Bug Report, [History plots] "Jump to current time" resets x range to 7d
|
I had put in a check which limits the range to 7d into the past if you press the "play" button, but now I'm not sure why this was needed. I removed it again and
things seem to be fine. Change is committed to develop.
Stefan |
13 Dec 2024, Marius Koeppel, Info, New Feature: Message Search
|
Dear all,
a new feature was implemented which allows to search the log messages in MIDAS. Attached one can find a more detailed explanation of how to use the feature.
If you see any issues / bugs feel don't hesitate to report them. For now the code was tested on Linux / Mac OS using Chrome, Firefox and Safari.
Best,
Marius |
18 Nov 2024, Lukas Gerritzen, Suggestion, Comma-separated indices in alarm conditions
|
I have the following use case: I would like to check if two elements of an array exceed a certain threshold.
However, they are not consecutive. Currently, I have to write two alarms, one checking Array[8] and one
checking Array [10].
It would be nice if we could enter conditions such as "/Path/To/Array[8,10] > 0.5".
I looked into the code of al_evaluate_condition() and it seems very C-style. I know that you have been
refactoring a lot of code to work with STL strings and their functions. If you find the time to refactor
alarm.cxx, I ask that you consider adding comma-separated lists as a new feature.
Cheers
Lukas |
10 Dec 2024, Stefan Ritt, Suggestion, Comma-separated indices in alarm conditions
|
These kind of alarm conditions have been implemented and committed. The documentation at
https://daq00.triumf.ca/MidasWiki/index.php/Alarm_System
has been updated.
/Stefan |
26 Nov 2024, Nick Hastings, Bug Report, TMFE::Sleep() errors
|
Hello,
I've noticed that SC FEs that use the TMFE class with midas-2022-05-c often report errors when calling TMFE:Sleep().
The error is :
[tmfe.cxx:1033:TMFE::Sleep,ERROR] select() returned -1, errno 22 (Invalid argument).
This seems to happen in two different ways:
1. Error being reported repeatedly
2. Occasional single errors being reported
When the first of these presents, we typically restart the FE to "solve" the problem.
Case 2. is typically ignored.
The code in question is:
void TMFE::Sleep(double time)
{
int status;
fd_set fdset;
struct timeval timeout;
FD_ZERO(&fdset);
timeout.tv_sec = time;
timeout.tv_usec = (time-timeout.tv_sec)*1000000.0;
while (1) {
status = select(1, &fdset, NULL, NULL, &timeout);
#ifdef EINTR
if (status < 0 && errno == EINTR) {
continue;
}
#endif
break;
}
if (status < 0) {
TMFE::Instance()->Msg(MERROR, "TMFE::Sleep", "select() returned %d, errno %d (%s)", status, errno, strerror(errno));
}
}
So it looks like either file descriptor of the timeval struct must have a problem.
From some reading it seems that invalid timeval structs are often caused by one or both
of tv_sec or tv_usec not being set. In the code above we can see that both appear to be
correctly set initially.
From the select() man page I see:
RETURN VALUE
On success, select() and pselect() return the number of file descriptors contained in
the three returned descriptor sets (that is, the total number of bits that are set in
readfds, writefds, exceptfds). The return value may be zero if the timeout expired
before any file descriptors became ready.
On error, -1 is returned, and errno is set to indicate the error; the file descriptor
sets are unmodified, and timeout becomes undefined.
The second paragraph quoted from the man page above would indicate to me that perhaps the
timeout needs to be reset inside the if block. eg:
if (status < 0 && errno == EINTR) {
timeout.tv_sec = time;
timeout.tv_usec = (time-timeout.tv_sec)*1000000.0;
continue;
}
Please note that I've only just briefly looked at this and was hoping someone more
familiar with using select() as a way to sleep() might be better able to understand
what is happening.
I wonder also if now that midas requires stricter/newer c++ standards if there maybe
some more straightforward method to sleep that is sufficiently robust and portable.
Thanks,
Nick. |
26 Nov 2024, Maia Henriksson-Ward, Bug Report, TMFE::Sleep() errors
|
> Hello,
>
> I've noticed that SC FEs that use the TMFE class with midas-2022-05-c often report errors when calling TMFE:Sleep().
> The error is :
>
> [tmfe.cxx:1033:TMFE::Sleep,ERROR] select() returned -1, errno 22 (Invalid argument).
>
> This seems to happen in two different ways:
>
> 1. Error being reported repeatedly
> 2. Occasional single errors being reported
>
> When the first of these presents, we typically restart the FE to "solve" the problem.
> Case 2. is typically ignored.
>
> The code in question is:
>
> void TMFE::Sleep(double time)
> {
> int status;
> fd_set fdset;
> struct timeval timeout;
>
> FD_ZERO(&fdset);
>
> timeout.tv_sec = time;
> timeout.tv_usec = (time-timeout.tv_sec)*1000000.0;
>
> while (1) {
> status = select(1, &fdset, NULL, NULL, &timeout);
> #ifdef EINTR
> if (status < 0 && errno == EINTR) {
> continue;
> }
> #endif
> break;
> }
>
> if (status < 0) {
> TMFE::Instance()->Msg(MERROR, "TMFE::Sleep", "select() returned %d, errno %d (%s)", status, errno, strerror(errno));
> }
> }
>
> So it looks like either file descriptor of the timeval struct must have a problem.
> From some reading it seems that invalid timeval structs are often caused by one or both
> of tv_sec or tv_usec not being set. In the code above we can see that both appear to be
> correctly set initially.
>
> From the select() man page I see:
>
> RETURN VALUE
> On success, select() and pselect() return the number of file descriptors contained in
> the three returned descriptor sets (that is, the total number of bits that are set in
> readfds, writefds, exceptfds). The return value may be zero if the timeout expired
> before any file descriptors became ready.
>
> On error, -1 is returned, and errno is set to indicate the error; the file descriptor
> sets are unmodified, and timeout becomes undefined.
>
> The second paragraph quoted from the man page above would indicate to me that perhaps the
> timeout needs to be reset inside the if block. eg:
>
> if (status < 0 && errno == EINTR) {
> timeout.tv_sec = time;
> timeout.tv_usec = (time-timeout.tv_sec)*1000000.0;
> continue;
> }
>
> Please note that I've only just briefly looked at this and was hoping someone more
> familiar with using select() as a way to sleep() might be better able to understand
> what is happening.
>
> I wonder also if now that midas requires stricter/newer c++ standards if there maybe
> some more straightforward method to sleep that is sufficiently robust and portable.
>
> Thanks,
>
> Nick.
I had the same error a few months ago, though I wasn't using a tagged release. It happened because I was calling TMFE::Sleep()
with a negative time. If your issues were caused by the same reason, TMFE::Sleep() can handle negative times since commit
591f78f (https://bitbucket.org/tmidas/midas/commits/591f78f52893d5ffd64bf4e52a1daac537ebd672).
Early in my debugging, I did come to the same conclusions you did, and actually tried a similar solution the one you suggested.
This was a few months ago and I didn't write down what happened, but I believe it didn't work because in my case the errno was
something other than EINTR, and/or the timeval was still an invalid argument for sleep because the timeout was still negative. I
never followed it up because I was able to fix my problem by fixing my frontend. |
27 Nov 2024, Konstantin Olchanski, Bug Report, TMFE::Sleep() errors
|
> [tmfe.cxx:1033:TMFE::Sleep,ERROR] select() returned -1, errno 22 (Invalid argument).
The very original copy of this function had an error and was spewing out this error quite often,
this was a missing handler for EINTR.
Now it looks like we are missing a handler for EINVAL.
Most likely sleep is called with a funny sleep time value that fills struct timeval with
values select() does not like.
I see Ben added a check for negative sleep times, and this is good.
I think I will do these changes:
a) add an error message for negative sleep time, I think user should never call ::Sleep with negative or zero sleep times and
if they do it is a bug and they should fix it, the error message will inform them so.
b) add a handler for EINVAL, which will report the requested sleep time and the values in struct timeval
K.O. |
27 Nov 2024, Konstantin Olchanski, Bug Report, TMFE::Sleep() errors
|
>
> I wonder also if now that midas requires stricter/newer c++ standards if there maybe
> some more straightforward method to sleep that is sufficiently robust and portable.
>
I believe POSIX defined clock_nanosleep() & co, so on most recent machines that is the most portable way to sleep.
Historically, select() was the only way to sleep for less than 1 sec, but it was never portable
because of differences between BSD UNIX and Linux implementations. (MacOS is BSD UNIX via FreeBSD).
On difference is the update of struct timeval is select() is interrupted.
In this elog entry, I compare sleep using select() with sleep using clock_nanosleep() and see that there is no difference:
https://daq00.triumf.ca/elog-midas/Midas/2115
As you can see tmfe.cxx has both implementations, select() and clock_nanosleep(), and anybody can try which one works better on
their computer.
K.O. |
27 Nov 2024, Konstantin Olchanski, Bug Report, TMFE::Sleep() errors
|
> status = select(1, &fdset, NULL, NULL, &timeout);
>
> On error, -1 is returned, ... timeout becomes undefined.
I have been reading "man select" for 30 years and I do not remember seeing this text.
I believe on BSD UNIX (MacOS) it says timeout is unchanged and on Linux is says timeout is updated to time actually slept.
I will have to investigate, but I suspect the man page was posix-ized, by sweeping BSD/MacOS and Linux implementations
under the same "instead of saying what it actually does, we will just say 'undefined'".
In any case, EINTR is not an error, it's an artefact of UNIX signal handling. Linux implementations always try
very hard to handle signals without causing EINTR to select(), read() and write(). This is most painful
when reading and writing to TCP sockets, because one most handle partial reads and EINTR.
K.O. |
06 Dec 2024, Konstantin Olchanski, Bug Report, TMFE::Sleep() errors
|
> > status = select(1, &fdset, NULL, NULL, &timeout);
> > On error, -1 is returned, ... timeout becomes undefined.
I confirm Linux and MacOS man pages and select() with EINTR work as I remember, Linux updates "timeout" to account for the
time already slept, MacOS does not ("timeout" is unchanged).
So the original code is roughly correct, but long sleeps will not work right if SIGALRM fires during sleeping.
Note that MIDAS no longer uses SIGALRM to fire cm_watchdog() (it was moved to a thread) and MIDAS does not use signals,
so handling of EINTR is now moot.
(Please correct me if I missed something).
The original bug report was about EINVAL, and best I can tell, it was caused by calls to TMFE::Sleep()
with strange sleep times that caused invalid values to be computed into the select() timeout.
To improve on this, I make these changes:
1) TMFE::Sleep(0) will report an error and will not sleep
2) TMFE::Sleep(negative number) will report an error and will not sleep
(please check the sleep time before calling TMFE::Sleep())
3) TMFE::Sleep(1 sec or less) will sleep using select(). (I also looked into using poll(), ppoll() and pselect()).
4) TMFE::Sleep(more than 1 second) will use a loop to sleep in increments of 1 second and will use one additional syscall to
read the current time to decide how much more to sleep.
5) if select() returns EINVAL, the error message will reporting the sleep time and the values in "timeout".
A side effect of this is that on both Linux and MacOS long sleeps work correctly if interrupted by SIGALRM,
because SIGALRM granularity is 1 sec and our sleep time is also 1 sec.
Commit [develop 06735d29] improve TMFE::Sleep()
K.O. |
06 Dec 2024, Konstantin Olchanski, Bug Report, TMFE::Sleep() errors
|
> Commit [develop 06735d29] improve TMFE::Sleep()
Report of test_sleep on Ubuntu-22 (Intel E-2236) and MacOS 14.6.1 (M1 MAX Mac Studio).
It is easy to see Ubuntu-22 (kernel 6.2.x) sleep granularity is ~60 usec, MacOS sleep granularity is <2 usec. (Sub 1 usec sleep likely measures the syscall() speed, 500 ns on Intel and 200
ns on ARM M1 MAX). (NOTE: long sleep is interrupted by an alarm roughly 10 seconds into the sleep, see progs/test_sleep.cxx)
daq00:midas$ uname -a
Linux daq00.triumf.ca 6.2.0-39-generic #40~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 16 10:53:04 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
daq00:midas$ ./bin/test_sleep
test short sleep:
sleep 10 loops, 100000.000 usec per loop, 1.000000000 sec total, 1.002568007 sec actual total, 100256.801 usec actual per loop, oversleep 256.801 usec, 0.3%
sleep 100 loops, 10000.000 usec per loop, 1.000000000 sec total, 1.025897980 sec actual total, 10258.980 usec actual per loop, oversleep 258.980 usec, 2.6%
sleep 1000 loops, 1000.000 usec per loop, 1.000000000 sec total, 1.169670105 sec actual total, 1169.670 usec actual per loop, oversleep 169.670 usec, 17.0%
sleep 10000 loops, 100.000 usec per loop, 1.000000000 sec total, 1.573357105 sec actual total, 157.336 usec actual per loop, oversleep 57.336 usec, 57.3%
sleep 99999 loops, 10.000 usec per loop, 0.999990000 sec total, 6.963442087 sec actual total, 69.635 usec actual per loop, oversleep 59.635 usec, 596.4%
sleep 1000000 loops, 1.000 usec per loop, 1.000000000 sec total, 60.939687967 sec actual total, 60.940 usec actual per loop, oversleep 59.940 usec, 5994.0%
sleep 1000000 loops, 0.100 usec per loop, 0.100000000 sec total, 0.613572121 sec actual total, 0.614 usec actual per loop, oversleep 0.514 usec, 513.6%
sleep 1000000 loops, 0.010 usec per loop, 0.010000000 sec total, 0.576359987 sec actual total, 0.576 usec actual per loop, oversleep 0.566 usec, 5663.6%
test long sleep: requested 126.000000000 sec ... sleeping ...
alarm!
test long sleep: requested 126.000000000 sec, actual 126.000875950 sec
bash-3.2$ uname -a
Darwin send.triumf.ca 23.6.0 Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000 arm64
bash-3.2$ ./bin/test_sleep
test short sleep:
sleep 10 loops, 100000.000 usec per loop, 1.000000000 sec total, 1.032556057 sec actual total, 103255.606 usec actual per loop, oversleep 3255.606 usec, 3.3%
sleep 100 loops, 10000.000 usec per loop, 1.000000000 sec total, 1.245460033 sec actual total, 12454.600 usec actual per loop, oversleep 2454.600 usec, 24.5%
sleep 1000 loops, 1000.000 usec per loop, 1.000000000 sec total, 1.331466913 sec actual total, 1331.467 usec actual per loop, oversleep 331.467 usec, 33.1%
sleep 10000 loops, 100.000 usec per loop, 1.000000000 sec total, 1.281141996 sec actual total, 128.114 usec actual per loop, oversleep 28.114 usec, 28.1%
sleep 99999 loops, 10.000 usec per loop, 0.999990000 sec total, 1.410759926 sec actual total, 14.108 usec actual per loop, oversleep 4.108 usec, 41.1%
sleep 1000000 loops, 1.000 usec per loop, 1.000000000 sec total, 2.400593996 sec actual total, 2.401 usec actual per loop, oversleep 1.401 usec, 140.1%
sleep 1000000 loops, 0.100 usec per loop, 0.100000000 sec total, 0.188431025 sec actual total, 0.188 usec actual per loop, oversleep 0.088 usec, 88.4%
sleep 1000000 loops, 0.010 usec per loop, 0.010000000 sec total, 0.188102007 sec actual total, 0.188 usec actual per loop, oversleep 0.178 usec, 1781.0%
test long sleep: requested 126.000000000 sec ... sleeping ...
alarm!
test long sleep: requested 126.000000000 sec, actual 126.001244068 sec
K.O. |
|