Back Midas Rome Roody Rootana
  Midas DAQ System, Page 133 of 138  Not logged in ELOG logo
New entries since:Wed Dec 31 16:00:00 1969
ID Date Author Topicup Subject
  2025   25 Nov 2020 Amy RobertsSuggestionODBSET wildcards with array keys in Sequencer files
I think the issue may be the version of MIDAS I'm using.  Mine is current as of February 4, 2020.  

But since then there have been changes to the sequencer code, specifically parts that handle indexing.

I'll try this out with an updated version of MIDAS and report back if there are still any issues after updating.

> I created some keys in my ODB to try to match yours.
> The ODBSET commands you wrote are all working fine (of course with different results), except only for the "/Detectors/Det*/Settings/Charge/Bias (V)*" which I will have to 
> look into.
> In any case the error message I'm getting is "could not match ay key" and not the one you are reporting.
> 
> Now I'm a bit puzzled:
> Are you sure your ODB contains those keys?
> Are you testing the ODBSET inside a more complex sequencer or on its own?
> 
> Maybe I can try to reproduce it using your ODB setup.
> Could you send an ODB dump of the "/Detectors" folder using the "save" command of odbedit ("cd /Detectors" and then "save detector.odb")?
> 
> Best,
> 
> Marco
> 
> 
> > The following all fail with "Cannot find ODB key "<key>""
> > 
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[*]" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[0-9]" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[1]" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)*" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)" 0
> > 
> > 
> > > Hi,
> > > I guess the issue is in the "[?]" part of the command, the indexing is handled differently from the odb path and does not 
> > > support "?".
> > > Are you trying to set only the first 9 channels?
> > > Could you try with "[*]" or "[0-9]" instead?
> > > 
> > > Marco
> > > 
> > > > I'm interested in using the matching feature for ODBSET explained on 
> > > > https://midas.triumf.ca/MidasWiki/index.php/Sequencer for settings that are in an 
> > > > array, like:
> > > > 
> > > > COMMENT "Ground the detectors"
> > > > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[?]" 0
> > > > 
> > > > Currently I get an error when I try to run this script.  Is this expected?  Would it 
> > > > be possible to implement matching for array values?
> > > > 
> > > > Thanks!
  2026   27 Nov 2020 Konstantin OlchanskiSuggestionODBSET wildcards with array keys in Sequencer files
> The following all fail with "Cannot find ODB key "<key>""
> 
> ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[*]" 0
> ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[0-9]" 0
> ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[1]" 0
> ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)*" 0
> ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)" 0
> 

It would be cool if ODB pattern matching in the sequencer
were consistent with the ODB pattern matching in the json-rpc
interface for web pages:

https://midas.triumf.ca/MidasWiki/index.php/Mjsonrpc#Supported_array_index_syntax

K.O.
  2034   27 Nov 2020 Konstantin OlchanskiSuggestioncmake build fixes
Hi, Alexandr, thank you for making improvements to MIDAS. I have some question
about your suggestions:

> there are several problems with current cmake build files in midas:
> - not all systems have cuda libs in /usr/local/cuda
> - not all cmake version like when redefining vars

we do not see these problems with the normal cmake on our current linux systems,
centos-7 and -8, Ubuntu LTS 18.04, 20.04.

so you have something different? can you be a bit more specific,
which version of cmake and which OS you have so see these troubles?

> - c++ standard not matching the one used to build ROOT
> - ROOTSYS is not needed to find ROOT (it is enough to have root in PATH)

Again ROOT tangles with the build of MIDAS.

MIDAS does not use ROOT. As a convenience to the users, we have a "ROOT output" driver
in mlogger and we build a special executable rmlogger with ROOT. Only this special
executable should be linked with ROOT and compiled with ROOT-specific flags.

The rest of the MIDAS build should not be affected by presence or absence of ROOT.

One would have to read old messages on this forum to understand this situation.

> 
> I have posted pull request 'https://bitbucket.org/tmidas/midas/pull-requests/17'
> which tries to fix some of the problems.
> Tests and comments are welcome.
>

I look at the diffs:

- CUDA detection is changed to "find_package(CUDA)". This code was added by Joseph and Ben, and there 
must be a reason why they did not use find_package(CUDA). They will have to sign-off on this change.

- ROOT related logic assumes that all of MIDAS will be built "the ROOT way". CFLAGS are changed, the C++ 
standard is changed, etc. this assumption is wrong. only rmlogger and rmana should be built "with ROOT".

If you want to follow through on this, I suggest that you split the pull request into two,
one pull request for the CUDA changes and one pull request for the ROOT changes. Also rework
your ROOT changes as I explained above (but also read all ROOT-related messages on this forum).

K.O.
  2038   30 Nov 2020 Marco FrancesconiSuggestionODBSET wildcards with array keys in Sequencer files
I totally agree that we should have a consistent formatting for array index expansion.
I had a look to the mjsonrpc code and I found the function parse_array_index_list(...) which does this job.
I have a similar function (adapted form previous code) in odb.cxx called strarrayindex(...) that is designed for the same "consistency" purposes between odbedit and sequencer.

Let me put few points that I noticed:
- mjsonrpc has a very different way to write the full array (no indexes given) while currently sequencer requires "[*]" to do the same (otherwise it only changes the first value of the array)
- currently the sequencer and the underlying ODB calls use two indexes (that are the same if you want to write only one key) so we will need a serious rewriting to allow something like "ODBSET array[1,3,5]"
- if I correctly understood the code, mjsonrpc instead generates a list of indices and then calls an ODB write on each of them. That's not always a good thing, for example if you are writing an array of n parameters on a DAQ 
board you will call the hotlink on that key n times
- in addition to that the sequencer will also have to cope with variable-based indexes like "ODBSET array[$val]", but then how it should parse something like "[$a,1]" or "[$a*]"?

For the very first point I do not see a clean way to do this without breaking the compatibility of existing sequencers or having a difference between the two implementations.
For the others I guess we can find a way out, however that's a major modification so I will put it on my todo list when I can find some free time.
In any case I would propose to merge the two functions, so we have only to maintain a single implementation of the parsing.

I guess it's a good moment to brainstorm about that, let me know what you think

Marco


> > The following all fail with "Cannot find ODB key "<key>""
> > 
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[*]" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[0-9]" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[1]" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)*" 0
> > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)" 0
> > 
> 
> It would be cool if ODB pattern matching in the sequencer
> were consistent with the ODB pattern matching in the json-rpc
> interface for web pages:
> 
> https://midas.triumf.ca/MidasWiki/index.php/Mjsonrpc#Supported_array_index_syntax
> 
> K.O.
  2040   30 Nov 2020 Stefan RittSuggestionODBSET wildcards with array keys in Sequencer files
Hi Konstantin,

we are considering to make the range selection uniform among json, sequencer and 
odbedit "set" command. Having multiple ranges like [1,4-5] will be quite some work, so 
my question is did you just implement it on the json side because it was easy, or are 
there experiments who really need it? Wouldn't it be enough to have

[*]
[n]
[n-m]

This way we always have only one db_set_data() value behind that. Any set of indices 
we have to split into several db_set_data(), which especially for the front-end 
configuration can cause trouble by triggering a hot link on each access.

Stefan
  2043   30 Nov 2020 Konstantin OlchanskiSuggestionODBSET wildcards with array keys in Sequencer files
> I totally agree that we should have a consistent formatting for array index expansion.
> I had a look to the mjsonrpc code and I found the function parse_array_index_list(...) which does this job.

Yes, it is good to review this stuff. I think the json-rpc call should accept more array index patterns:

a[*] - whole array (even though it is unnatural use in javascript, we do not say "let a[*] = b[*]", we say "let a = b".
a[5-] - from 5th element to the end (in the case we do not know the length)
a[-10] - from first element to element 10 (this is same as a[0-10], but needed for consistency with previous case).

K.O.

> I have a similar function (adapted form previous code) in odb.cxx called strarrayindex(...) that is designed for the same "consistency" purposes between odbedit and sequencer.
> 
> Let me put few points that I noticed:
> - mjsonrpc has a very different way to write the full array (no indexes given) while currently sequencer requires "[*]" to do the same (otherwise it only changes the first value of the array)
> - currently the sequencer and the underlying ODB calls use two indexes (that are the same if you want to write only one key) so we will need a serious rewriting to allow something like "ODBSET array[1,3,5]"
> - if I correctly understood the code, mjsonrpc instead generates a list of indices and then calls an ODB write on each of them. That's not always a good thing, for example if you are writing an array of n parameters on a DAQ 
> board you will call the hotlink on that key n times
> - in addition to that the sequencer will also have to cope with variable-based indexes like "ODBSET array[$val]", but then how it should parse something like "[$a,1]" or "[$a*]"?
> 
> For the very first point I do not see a clean way to do this without breaking the compatibility of existing sequencers or having a difference between the two implementations.
> For the others I guess we can find a way out, however that's a major modification so I will put it on my todo list when I can find some free time.
> In any case I would propose to merge the two functions, so we have only to maintain a single implementation of the parsing.
> 
> I guess it's a good moment to brainstorm about that, let me know what you think
> 
> Marco
> 
> 
> > > The following all fail with "Cannot find ODB key "<key>""
> > > 
> > > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[*]" 0
> > > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[0-9]" 0
> > > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)[1]" 0
> > > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)*" 0
> > > ODBSET "/Detectors/Det*/Settings/Charge/Bias (V)" 0
> > > 
> > 
> > It would be cool if ODB pattern matching in the sequencer
> > were consistent with the ODB pattern matching in the json-rpc
> > interface for web pages:
> > 
> > https://midas.triumf.ca/MidasWiki/index.php/Mjsonrpc#Supported_array_index_syntax
> > 
> > K.O.
  2044   30 Nov 2020 Konstantin OlchanskiSuggestionODBSET wildcards with array keys in Sequencer files
> 
> we are considering to make the range selection uniform among json, sequencer and 
> odbedit "set" command. Having multiple ranges like [1,4-5] will be quite some work, so 
> my question is did you just implement it on the json side because it was easy, or are 
> there experiments who really need it? Wouldn't it be enough to have
> 
> [*]
> [n]
> [n-m]
> 

It has been a long time, but most likely I designed the interface to work this
way to permit maximum flexibility for writing into an array using just one
rpc operation.

The generalized form is:
[range,range,range...]

where range is:
array index or
index1-index2 or
index2-index1 (write in reverse order)

This is all documented here:
https://midas.triumf.ca/MidasWiki/index.php/Mjsonrpc#Supported_array_index_syntax

I think it is too late to change it.

>
> This way we always have only one db_set_data() value behind that. Any set of indices 
> we have to split into several db_set_data()
> 

Sounds good. I think it is easy to have a common implementation:

One would need following functions:
parse range selection from string into std::vector<int> of array indices (we already have it)
call db_set_data_range() (this is easy to add).

>
> trouble by triggering a hot link on each access.
>

There is no escaping this trouble. Half the experiments want notification
"per array", the other half, "per array element". We cannot choose one or the other for them,
we have to provide a way for the user to say how they want it.

P.S. With existing ODB calls, you cannot do [n-m] ranges. You can do whole array or you
can do one-element-at-a-time.

K.O.
  2061   17 Dec 2020 Amy RobertsSuggestionImproving variable functionality in Sequencer?
We're using the sequencer to manage runs, and this typically looks something like:

1. save ODB keys to variables via ODBGET
2. set ODB keys to new values for a "pre-run" process
3. return ODB keys to values created in line 1
4. take data

The problem I'm running into is that the list of ODB keys to save is pretty 
unwieldy.  I'm wondering if there are sequencer features that exist or that I could 
request that might make this easier.

For example, having a way to list ODB keys, save ODB directories, and load ODB 
directories would be much more concise way for me to write my script.

Another option might be to have some version of the ODBSET wildcards for ODBGET.  
Although for this, setting the variable names might be tricky.  

In any case, even being able to ODBGET an array and set that to one variable name 
would be a big improvement.
  2062   18 Dec 2020 Stefan RittSuggestionCode formatting
May I ask for your quick opinion on code formatting. MIDAS had a coding style 
which pretty much followed the ROOT coding style described at

https://root.cern/contribute/coding_conventions/

so we followed the "3 spaces indent" convention, braces according to Kernigham & 
Ritchie and a few other things. I see however that code written by different 
people still is formatted differently, like spaces before and after comparators 
etc. I wonder if it would make sense to keep a consistent code formatting through 
the whole midas repository.

Looking again at what the ROOT guys doe (see link above), they have a ClangFormat 
file, which I attached to this post. Putting this file into the root of midas 
ensures that all files are formatted in exactly the same way, which would increase 
readability largely.

The nice thing with ClangFormat is that can be integrated into my editor (Clion) as 
well as in emacs and vim:

https://clang.llvm.org/docs/ClangFormat.html

This would also make the emacs settings in our files obsolete:

/* emacs
 * Local Variables:
 * tab-width: 8
 * c-basic-offset: 3
 * indent-tabs-mode: nil
 * End:
 */

I don't like these because they are only for people using emacs. If everybody would 
put statements into the files with their favourite editor, all our source files 
would be cluttered quite a bit.

So the question is now how style to use? I attached different trials with a simple 
file from the distribution, so you can see the differences. They use the style from

- LLVM
- ROOT
- GNU
- Google

I consciously skipped the "Microsoft" style ;-)

Which one should we settle on? Any opinion? If I don't hear anything, I will pick a 
style at the end of this year 2020. I have a slight favour of the ROOT style, although 
I don't like that the "case" is not indented there under the opening brace of the 
switch statement which seems inconsistent to me. The only one doing that right is the 
Google format, but that one has an indentation of 2 chars instead our usual 3 chars. 
At the end of the day I think it's not so important on which style we agree, as long 
as we DO have a common style for all midas files.

Best,
Stefan
Attachment 1: .clang-format
---
Language:        Cpp
# BasedOnStyle:  LLVM
AccessModifierOffset: -3
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
# This would be nice to have but seems to also (mis)align function parameters
AlignConsecutiveDeclarations: true
AlignEscapedNewlinesLeft: true
AlignOperands:   true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
# This option is "deprecated and is retained for backwards compatibility."
# AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
  AfterClass:      false
  AfterControlStatement: false
  AfterEnum:       false
  AfterFunction:   true
  AfterNamespace:  false
  AfterObjCDeclaration: false
  AfterStruct:     false
  AfterUnion:      false
  BeforeCatch:     false
  BeforeElse:      false
  IndentBraces:    false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit:     120
CommentPragmas:  '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 3
ContinuationIndentWidth: 3
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat:   false
ExperimentalAutoDetectBinPacking: false
ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
  - Regex:           '^("|<)T'
    Priority:        4
  - Regex:           '^("|<)ROOT/'
    Priority:        5
  - Regex:           '^<.*\.h>'
    Priority:        1
  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
    Priority:        2
  - Regex:           '^(<|"(gtest|isl|json)/)'
    Priority:        3
  - Regex:           '.*'
    Priority:        6
IndentCaseLabels: false
IndentWidth:     3
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd:   ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 3
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60000
PointerAlignment: Right
ReflowComments:  true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles:  false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard:        Cpp11
TabWidth:        3
UseTab:          Never
...
Attachment 2: cnaf_callback_llvm.cxx
/********************************************************************\

  Name:         cnaf_callback.c
  Created by:   Stefan Ritt
  Created by:   moved here from mfe.c by Konstantin Olchanski

  Contents:     The system part of the MIDAS frontend. Has to be
                linked with user code to form a complete frontend

  $Id$

\********************************************************************/

#include <stdio.h>
#include <assert.h>
#include "midas.h"
#include "msystem.h"
#include "mcstd.h"

/*------------------------------------------------------------------*/

static int cnaf_debug = 0;

INT cnaf_callback(INT index, void *prpc_param[]) {
  DWORD cmd, b, c, n, a, f, *pdword, *size, *x, *q, dtemp;
  WORD *pword, *pdata, temp;
  INT i, count;

  /* Decode parameters */
  cmd = CDWORD(0);
  b = CDWORD(1);
  c = CDWORD(2);
  n = CDWORD(3);
  a = CDWORD(4);
  f = CDWORD(5);
  pdword = CPDWORD(6);
  pword = CPWORD(6);
  pdata = CPWORD(6);
  size = CPDWORD(7);
  x = CPDWORD(8);
  q = CPDWORD(9);

  /* determine repeat count */
  if (index == RPC_CNAF16)
    count = *size / sizeof(WORD); /* 16 bit */
  else
    count = *size / sizeof(DWORD); /* 24 bit */

  switch (cmd) {
    /*---- special commands ----*/

  case CNAF_INHIBIT_SET: cam_inhibit_set(c);
    break;
  case CNAF_INHIBIT_CLEAR: cam_inhibit_clear(c);
    break;
  case CNAF_CRATE_CLEAR: cam_crate_clear(c);
    break;
  case CNAF_CRATE_ZINIT: cam_crate_zinit(c);
    break;

  case CNAF_TEST: break;

  case CNAF:
    if (index == RPC_CNAF16) {
      for (i = 0; i < count; i++)
        if (f < 16)
          cam16i_q(c, n, a, f, pword++, (int *) x, (int *) q);
        else if (f < 24)
          cam16o_q(c, n, a, f, pword[i], (int *) x, (int *) q);
        else
          cam16i_q(c, n, a, f, &temp, (int *) x, (int *) q);
    } else {
      for (i = 0; i < count; i++)
        if (f < 16)
          cam24i_q(c, n, a, f, pdword++, (int *) x, (int *) q);
        else if (f < 24)
          cam24o_q(c, n, a, f, pdword[i], (int *) x, (int *) q);
        else
          cam24i_q(c, n, a, f, &dtemp, (int *) x, (int *) q);
    }

    break;

  case CNAF_nQ:
    if (index == RPC_CNAF16) {
      if (f < 16) {
        cam16i_rq(c, n, a, f, &pword, count);
        *size = (POINTER_T) pword - (POINTER_T) pdata;
      }
    } else {
      if (f < 16) {
        cam24i_rq(c, n, a, f, &pdword, count);
        *size = (POINTER_T) pdword - (POINTER_T) pdata;
      }
    }

    /* return reduced return size */
    break;

  default: printf("cnaf: Unknown command 0x%X\n", (unsigned int) cmd);
  }

  if (cnaf_debug) {
    if (index == RPC_CNAF16)
      printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int) cmd, (int) count, (int) c, (int) n, (int) a,
             (int) f, (int) pword[0], (int) *x, (int) *q);
    else if (index == RPC_CNAF24)
      printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int) cmd, (int) count, (int) c, (int) n, (int) a,
             (int) f, (int) pdword[0], (int) *x, (int) *q);
  }

  return RPC_SUCCESS;
}

void register_cnaf_callback(int debug) {
  cnaf_debug = debug;
  /* register CNAF callback */
  cm_register_function(RPC_CNAF16, cnaf_callback);
  cm_register_function(RPC_CNAF24, cnaf_callback);
}

/* end file */
Attachment 3: cnaf_callback_root.cxx
/********************************************************************\

  Name:         cnaf_callback.c
  Created by:   Stefan Ritt
  Created by:   moved here from mfe.c by Konstantin Olchanski

  Contents:     The system part of the MIDAS frontend. Has to be
                linked with user code to form a complete frontend

  $Id$

\********************************************************************/

#include <stdio.h>
#include <assert.h>
#include "midas.h"
#include "msystem.h"
#include "mcstd.h"

/*------------------------------------------------------------------*/

static int cnaf_debug = 0;

INT cnaf_callback(INT index, void *prpc_param[])
{
   DWORD cmd, b, c, n, a, f, *pdword, *size, *x, *q, dtemp;
   WORD *pword, *pdata, temp;
   INT   i, count;

   /* Decode parameters */
   cmd    = CDWORD(0);
   b      = CDWORD(1);
   c      = CDWORD(2);
   n      = CDWORD(3);
   a      = CDWORD(4);
   f      = CDWORD(5);
   pdword = CPDWORD(6);
   pword  = CPWORD(6);
   pdata  = CPWORD(6);
   size   = CPDWORD(7);
   x      = CPDWORD(8);
   q      = CPDWORD(9);

   /* determine repeat count */
   if (index == RPC_CNAF16)
      count = *size / sizeof(WORD); /* 16 bit */
   else
      count = *size / sizeof(DWORD); /* 24 bit */

   switch (cmd) {
      /*---- special commands ----*/

   case CNAF_INHIBIT_SET: cam_inhibit_set(c); break;
   case CNAF_INHIBIT_CLEAR: cam_inhibit_clear(c); break;
   case CNAF_CRATE_CLEAR: cam_crate_clear(c); break;
   case CNAF_CRATE_ZINIT: cam_crate_zinit(c); break;

   case CNAF_TEST: break;

   case CNAF:
      if (index == RPC_CNAF16) {
         for (i = 0; i < count; i++)
            if (f < 16)
               cam16i_q(c, n, a, f, pword++, (int *)x, (int *)q);
            else if (f < 24)
               cam16o_q(c, n, a, f, pword[i], (int *)x, (int *)q);
            else
               cam16i_q(c, n, a, f, &temp, (int *)x, (int *)q);
      } else {
         for (i = 0; i < count; i++)
            if (f < 16)
               cam24i_q(c, n, a, f, pdword++, (int *)x, (int *)q);
            else if (f < 24)
               cam24o_q(c, n, a, f, pdword[i], (int *)x, (int *)q);
            else
               cam24i_q(c, n, a, f, &dtemp, (int *)x, (int *)q);
      }

      break;

   case CNAF_nQ:
      if (index == RPC_CNAF16) {
         if (f < 16) {
            cam16i_rq(c, n, a, f, &pword, count);
            *size = (POINTER_T)pword - (POINTER_T)pdata;
         }
      } else {
         if (f < 16) {
            cam24i_rq(c, n, a, f, &pdword, count);
            *size = (POINTER_T)pdword - (POINTER_T)pdata;
         }
      }

      /* return reduced return size */
      break;

   default: printf("cnaf: Unknown command 0x%X\n", (unsigned int)cmd);
   }

   if (cnaf_debug) {
      if (index == RPC_CNAF16)
         printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int)cmd, (int)count, (int)c, (int)n, (int)a,
                (int)f, (int)pword[0], (int)*x, (int)*q);
      else if (index == RPC_CNAF24)
         printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int)cmd, (int)count, (int)c, (int)n, (int)a,
                (int)f, (int)pdword[0], (int)*x, (int)*q);
   }

   return RPC_SUCCESS;
}

void register_cnaf_callback(int debug)
{
   cnaf_debug = debug;
   /* register CNAF callback */
   cm_register_function(RPC_CNAF16, cnaf_callback);
   cm_register_function(RPC_CNAF24, cnaf_callback);
}

/* end file */
Attachment 4: cnaf_callback_gnu.cxx
/********************************************************************\

  Name:         cnaf_callback.c
  Created by:   Stefan Ritt
  Created by:   moved here from mfe.c by Konstantin Olchanski

  Contents:     The system part of the MIDAS frontend. Has to be
                linked with user code to form a complete frontend

  $Id$

\********************************************************************/

#include <stdio.h>
#include <assert.h>
#include "midas.h"
#include "msystem.h"
#include "mcstd.h"

/*------------------------------------------------------------------*/

static int cnaf_debug = 0;

INT cnaf_callback (INT index, void *prpc_param[])
{
  DWORD cmd, b, c, n, a, f, *pdword, *size, *x, *q, dtemp;
  WORD *pword, *pdata, temp;
  INT i, count;

  /* Decode parameters */
  cmd = CDWORD(0);
  b = CDWORD(1);
  c = CDWORD(2);
  n = CDWORD(3);
  a = CDWORD(4);
  f = CDWORD(5);
  pdword = CPDWORD(6);
  pword = CPWORD(6);
  pdata = CPWORD(6);
  size = CPDWORD(7);
  x = CPDWORD(8);
  q = CPDWORD(9);

  /* determine repeat count */
  if (index == RPC_CNAF16)
    count = *size / sizeof (WORD); /* 16 bit */
  else
    count = *size / sizeof (DWORD); /* 24 bit */

  switch (cmd)
    {
      /*---- special commands ----*/

  case CNAF_INHIBIT_SET: cam_inhibit_set (c);
      break;
  case CNAF_INHIBIT_CLEAR: cam_inhibit_clear (c);
      break;
  case CNAF_CRATE_CLEAR: cam_crate_clear (c);
      break;
  case CNAF_CRATE_ZINIT: cam_crate_zinit (c);
      break;

  case CNAF_TEST: break;

  case CNAF:
    if (index == RPC_CNAF16)
      {
        for (i = 0; i < count; i++)
          if (f < 16)
            cam16i_q (c, n, a, f, pword++, (int *) x, (int *) q);
          else if (f < 24)
            cam16o_q (c, n, a, f, pword[i], (int *) x, (int *) q);
          else
            cam16i_q (c, n, a, f, &temp, (int *) x, (int *) q);
      }
    else
      {
        for (i = 0; i < count; i++)
          if (f < 16)
            cam24i_q (c, n, a, f, pdword++, (int *) x, (int *) q);
          else if (f < 24)
            cam24o_q (c, n, a, f, pdword[i], (int *) x, (int *) q);
          else
            cam24i_q (c, n, a, f, &dtemp, (int *) x, (int *) q);
      }

      break;

  case CNAF_nQ:
    if (index == RPC_CNAF16)
      {
        if (f < 16)
          {
            cam16i_rq (c, n, a, f, &pword, count);
            *size = (POINTER_T) pword - (POINTER_T) pdata;
          }
      }
    else
      {
        if (f < 16)
          {
            cam24i_rq (c, n, a, f, &pdword, count);
            *size = (POINTER_T) pdword - (POINTER_T) pdata;
          }
      }

      /* return reduced return size */
      break;

  default: printf ("cnaf: Unknown command 0x%X\n", (unsigned int) cmd);
    }

  if (cnaf_debug)
    {
      if (index == RPC_CNAF16)
        printf ("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int) cmd, (int) count, (int) c, (int) n, (int) a,
                (int) f, (int) pword[0], (int) *x, (int) *q);
      else if (index == RPC_CNAF24)
        printf ("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int) cmd, (int) count, (int) c, (int) n, (int) a,
                (int) f, (int) pdword[0], (int) *x, (int) *q);
    }

  return RPC_SUCCESS;
}

void register_cnaf_callback (int debug)
{
  cnaf_debug = debug;
  /* register CNAF callback */
  cm_register_function (RPC_CNAF16, cnaf_callback);
  cm_register_function (RPC_CNAF24, cnaf_callback);
}

/* end file */
Attachment 5: cnaf_callback_google.cxx
/********************************************************************\

  Name:         cnaf_callback.c
  Created by:   Stefan Ritt
  Created by:   moved here from mfe.c by Konstantin Olchanski

  Contents:     The system part of the MIDAS frontend. Has to be
                linked with user code to form a complete frontend

  $Id$

\********************************************************************/

#include <stdio.h>
#include <assert.h>
#include "midas.h"
#include "msystem.h"
#include "mcstd.h"

/*------------------------------------------------------------------*/

static int cnaf_debug = 0;

INT cnaf_callback(INT index, void *prpc_param[]) {
  DWORD cmd, b, c, n, a, f, *pdword, *size, *x, *q, dtemp;
  WORD *pword, *pdata, temp;
  INT i, count;

  /* Decode parameters */
  cmd = CDWORD(0);
  b = CDWORD(1);
  c = CDWORD(2);
  n = CDWORD(3);
  a = CDWORD(4);
  f = CDWORD(5);
  pdword = CPDWORD(6);
  pword = CPWORD(6);
  pdata = CPWORD(6);
  size = CPDWORD(7);
  x = CPDWORD(8);
  q = CPDWORD(9);

  /* determine repeat count */
  if (index == RPC_CNAF16)
    count = *size / sizeof(WORD); /* 16 bit */
  else
    count = *size / sizeof(DWORD); /* 24 bit */

  switch (cmd) {
    /*---- special commands ----*/

    case CNAF_INHIBIT_SET: cam_inhibit_set(c);
      break;
    case CNAF_INHIBIT_CLEAR: cam_inhibit_clear(c);
      break;
    case CNAF_CRATE_CLEAR: cam_crate_clear(c);
      break;
    case CNAF_CRATE_ZINIT: cam_crate_zinit(c);
      break;

    case CNAF_TEST: break;

    case CNAF:
      if (index == RPC_CNAF16) {
        for (i = 0; i < count; i++)
          if (f < 16)
            cam16i_q(c, n, a, f, pword++, (int *) x, (int *) q);
          else if (f < 24)
            cam16o_q(c, n, a, f, pword[i], (int *) x, (int *) q);
          else
            cam16i_q(c, n, a, f, &temp, (int *) x, (int *) q);
      } else {
        for (i = 0; i < count; i++)
          if (f < 16)
            cam24i_q(c, n, a, f, pdword++, (int *) x, (int *) q);
          else if (f < 24)
            cam24o_q(c, n, a, f, pdword[i], (int *) x, (int *) q);
          else
            cam24i_q(c, n, a, f, &dtemp, (int *) x, (int *) q);
      }

      break;

    case CNAF_nQ:
      if (index == RPC_CNAF16) {
        if (f < 16) {
          cam16i_rq(c, n, a, f, &pword, count);
          *size = (POINTER_T) pword - (POINTER_T) pdata;
        }
      } else {
        if (f < 16) {
          cam24i_rq(c, n, a, f, &pdword, count);
          *size = (POINTER_T) pdword - (POINTER_T) pdata;
        }
      }

      /* return reduced return size */
      break;

    default: printf("cnaf: Unknown command 0x%X\n", (unsigned int) cmd);
  }

  if (cnaf_debug) {
    if (index == RPC_CNAF16)
      printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int) cmd, (int) count, (int) c, (int) n, (int) a,
             (int) f, (int) pword[0], (int) *x, (int) *q);
    else if (index == RPC_CNAF24)
      printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n", (int) cmd, (int) count, (int) c, (int) n, (int) a,
             (int) f, (int) pdword[0], (int) *x, (int) *q);
  }

  return RPC_SUCCESS;
}

void register_cnaf_callback(int debug) {
  cnaf_debug = debug;
  /* register CNAF callback */
  cm_register_function(RPC_CNAF16, cnaf_callback);
  cm_register_function(RPC_CNAF24, cnaf_callback);
}

/* end file */
  2063   04 Jan 2021 Stefan RittSuggestionCode formatting
After pondering over the holidays, I decided to use the widely used LLVM code formatting, 
just adapted slightly for 3 spaces and "case" indentation in a "switch" statement. This 
formatting is now very close to our original one. Nevertheless, I did not reformat all 
existing code, since that would screw up the git repository, and you cannot see then anymore 
who wrote which line of code. But having the .clang-format file now in the midas root, all 
NEW files fill follow that standard. 

The CLion editor automatically picks up the .clang-format file if your enable ClangFomrat 
via Preferences -> Code Style -> General -> Enable ClangFormat.

EMACS can also use this file by adding following lines to your .emacs:

(load "<path-to-clang>/tools/clang-format/clang-format.el")
(global-set-key [C-M-tab] 'clang-format-region)

One problem left is if you check out midas on a new machine, you might not have there your 
personal .emacs file. If there is a way to ship a .emacs with midas, which gets 
automatically loaded, I would be happy to put this into the distribution.

Stefan
Attachment 1: .clang-format
# Generated from CLion C/C++ Code Style settings
BasedOnStyle: LLVM
AccessModifierOffset: -3
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignOperands: false
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: MultiLine
BreakBeforeBraces: Custom
BraceWrapping:
  AfterCaseLabel: false
  AfterClass: false
  AfterControlStatement: Never
  AfterEnum: false
  AfterFunction: false
  AfterNamespace: false
  AfterUnion: false
  BeforeCatch: false
  BeforeElse: false
  IndentBraces: false
  SplitEmptyFunction: false
  SplitEmptyRecord: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 3
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 3
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Right
ReflowComments: false
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 3
UseTab: Never
  2065   05 Jan 2021 Amy RobertsSuggestionImproving variable functionality in Sequencer?
Hello, just wanted to re-ping on this question now that folks are starting to get back from 
the holidays.
  2067   06 Jan 2021 Stefan RittSuggestionImproving variable functionality in Sequencer?
I guess you use a wrong pattern here. There is no need to copy ODB values to local variables, 
then change them, then write them back. You can rather directly write values to the ODB. We run 
all our experiments in that way and we can do what we want. So most of our scripts have sections 
like

 ODBSUBDIR "/Equipment/Laser/Variables"
   ODBSET "Setting[*]", 0, 0
   ODBSET "Output[1]", 0, 0
   ODBSET "Output[2]", 1, 0
   ODBSET "Output[3]", 0, 0
   ODBSET "Output[4]", 1, 1
 ENDODBSUBDIR

Note that both the path and the indices can contain wild cards, making this pattern more 
flexible. Wildcards are however not (yet) supported for local variables, that's why we use 
directly the ODBSET directive.

I attach a larger example from the MEG experiment here for your reference.

Stefan
Attachment 1: laser.msl
COMMENT "TC laser run"
# include XEC setting script
INCLUDE xec_settings


  RUNDESCRIPTION "TC laser run"

  SET Nrun, 1
  SET Freq, 1200

  ODBSET "/Experiment/Run Parameters/SQL/SPX/SPXConfId", 79, 0

  ODBGET "/Sequencer/Variables/Freq", Freq
  ODBSET "/Sequencer/Variables/Freq", 1200, 1

  SET Power, 12
  SET Temp, 15
  SET LaserFreq, 40
  SET Nevent, 3000

  ODBGET "/Sequencer/Variables/Nevent", Nevent
  ODBSET "/Sequencer/Variables/Nevent", 3000, 1

  SET Gain, 100
  SET PzcLevel, 7

  # Reset XEC setting
  CALL setup_ADC, 0
  CALL setup_DRS, 0


  ODBSUBDIR "/Equipment/Trigger/Settings/WaveDAQ/"
  ODBSET "AUXCrate/AUX-13/SamplingFrequency", $Freq, 0
  ODBSET "AUXCrate/AUX-13/FrontendPzcLevel", 3, 0
  ODBSET "AUXCrate/AUX-13/FrontendGain[14]", 1, 0
  ODBSET "AUXCrate/AUX-13/FrontendPzc[14]", n, 0
  ODBSET "AUXCrate/AUX-13/FrontendGain[15]", 1, 0
  ODBSET "AUXCrate/AUX-13/FrontendPzc[15]", y, 0
  ODBSET "AUXCrate/AUX-13/DRSChannelTxEnable", 0x3FFFFx,0
  ODBSET "AUXCrate/AUX-13/ZeroSuppressionEnable", n, 0

  ODBSET "TCUS1Crate/TU1-*/SamplingFrequency", $Freq, 0
  ODBSET "TCUS1Crate/TU1-*/FrontendPzcLevel", $PzcLevel, 0
  ODBSET "TCUS1Crate/TU1-*/FrontendPzc[*]", y, 0
  ODBSET "TCUS1Crate/TU1-*/DRSChannelTxEnable", 0x3FFFF,0
  ODBSET "TCUS1Crate/TU1-*/ZeroSuppressionEnable", n, 0

  ODBSET "TCUS1Crate/TU1-*/FrontendGain[*]", $Gain, 0
  ODBSET "TCUS1Crate/TU1-6/FrontendGain[*]", 50, 0
  ODBSET "TCUS1Crate/TU1-*/TDCChannelTxEnable", 0,0

  ENDODBSUBDIR


  ODBSUBDIR "/Equipment/Trigger/Settings/WaveDAQ"
  ODBSET "Trigger/MASTER/TriggerPrescaling[*]", 0, 0
  ODBSET "Trigger/MASTER/TriggerEnable[*]", n, 0
  ODBSET "Trigger/MASTER/TriggerPrescaling[63]", 1, 0
  ODBSET "Trigger/MASTER/TriggerEnable[63]", y, 0
  ODBSET "Trigger/MASTER/Write TRGC", y, 0 
  ODBSET "Trigger/MASTER/Write TGEN", y, 0  
  ODBSET "Trigger/MASTER/Write XEC", n, 0 
  ODBSet "Trigger/TC/Write SPX", n, 0
  ENDODBSUBDIR

  ODBSUBDIR ""
  ENDODBSUBDIR

  ODBSET "/Equipment/Trigger/Settings/Reload all", y, 1

  WAIT ODBValue, "/Equipment/Trigger/Variables/Config busy", ==, 0
  WAIT seconds, 2

  ODBSUBDIR "/Equipment/Trigger/Settings/WaveDAQ"
  ODBSET "Trigger/MASTER/TriggerPrescaling[*]", 0, 0
  ODBSET "Trigger/MASTER/TriggerEnable[*]", n, 0
  ODBSET "Trigger/MASTER/TriggerPrescaling[63]", 100, 0
  ODBSET "Trigger/MASTER/TriggerEnable[63]", y, 1
  ENDODBSUBDIR

  ODBSUBDIR ""
  ENDODBSUBDIR

  CAT description, "TC pedestal"
  ODBSET "/Experiment/Run Parameters/Run description", $description, 1

   LOOP $Nrun
      TRANSITION start
      WAIT events, 1000
      TRANSITION stop
   ENDLOOP


  ODBSUBDIR "/Equipment/Trigger/Settings/WaveDAQ"
  ODBSET "Trigger/MASTER/TriggerPrescaling[*]", 0, 0
  ODBSET "Trigger/MASTER/TriggerEnable[*]", n, 0
  ODBSET "Trigger/MASTER/TriggerPrescaling[23]", 1, 0
  ODBSET "Trigger/MASTER/TriggerEnable[23]", y, 1
  ENDODBSUBDIR



  ODBSUBDIR "/Equipment/Laser/Variables/"
  ODBSET "Output[0]", $LaserFreq, 0
  ODBSET "Output[1]", 1, 0
  ODBSET "Output[2]", 1, 0
  ODBSET "Output[3]", 1, 0
  ODBSET "Output[4]", 0, 1
  ENDODBSUBDIR
  WAIT seconds, 10

  ODBSUBDIR ""
  ENDODBSUBDIR
  CAT description, "Laser run, sector: 4, frequency: ", $Freq, ", clock: square, power: ", $Power, ", attenuator: 0, temperature: ", $Temp
  ODBSET "/Experiment/Run Parameters/Run description", $description, 1

   LOOP $Nrun
      TRANSITION start
      WAIT events, $Nevent
      TRANSITION stop
   ENDLOOP



  ODBSUBDIR "/Equipment/Laser/Variables/"
  ODBSET "Output[1]", 1, 0
  ODBSET "Output[2]", 0, 0
  ODBSET "Output[3]", 0, 0
  ODBSET "Output[4]", 1, 1
  ENDODBSUBDIR
  WAIT seconds, 10

  ODBSUBDIR ""
  ENDODBSUBDIR
  CAT description, "Laser run, sector: 5, frequency: ", $Freq, ", clock: square, power: ", $Power, ", attenuator: 0, temperature: ", $Temp
  ODBSET "/Experiment/Run Parameters/Run description", $description, 1

   LOOP $Nrun
      TRANSITION start
      WAIT events, $Nevent
      TRANSITION stop
   ENDLOOP




  ODBSUBDIR "/Equipment/Laser/Variables"
  ODBSET "Output[1]", 0, 0
  ODBSET "Output[2]", 1, 0
  ODBSET "Output[3]", 0, 0
  ODBSET "Output[4]", 1, 1
  ENDODBSUBDIR
  WAIT seconds, 10

  ODBSUBDIR ""
  ENDODBSUBDIR
  CAT description, "Laser run, sector: 6, frequency: ", $Freq, ", clock: square, power: ", $Power, ", attenuator: 0, temperature: ", $Temp 
  ODBSET "/Experiment/Run Parameters/Run description", $description, 1

   LOOP $Nrun
      TRANSITION start
      WAIT events, $Nevent
      TRANSITION stop
   ENDLOOP

  ODBSUBDIR "/Equipment/Laser/Variables"
  ODBSET "Output[1]", 1, 0
  ODBSET "Output[2]", 1, 0
  ODBSET "Output[3]", 0, 0
  ODBSET "Output[4]", 1, 1
  ENDODBSUBDIR
  WAIT seconds, 10

  ODBSUBDIR ""
  ENDODBSUBDIR
  CAT description, "Laser run, sector: 7, frequency: ", $Freq, ", clock: square, power: ", $Power, ", attenuator: 0, temperature: ", $Temp
  ODBSET "/Experiment/Run Parameters/Run description", $description, 1

   LOOP $Nrun
      TRANSITION start
      WAIT events, $Nevent
      TRANSITION stop
   ENDLOOP

  ODBSUBDIR "/Equipment/Laser/Variables"
  ODBSET "Output[0]", 0, 0
  ODBSET "Output[1]", 1, 0
  ODBSET "Output[2]", 0, 0
  ODBSET "Output[3]", 0, 0
  ODBSET "Output[4]", 0, 1
  ENDODBSUBDIR

  ODBSUBDIR "/Equipment/Trigger/Settings/WaveDAQ"
  ODBSET "AUXCrate/AUX-13/SamplingFrequency", 1200, 0
  ODBSET "AUXCrate/AUX-13/FrontendPzcLevel", 7, 0
  ODBSET "AUXCrate/AUX-13/FrontendGain[14]", 1, 0
  ODBSET "AUXCrate/AUX-13/FrontendPzc[14]", n, 0
  ODBSET "AUXCrate/AUX-13/FrontendGain[15]", 1, 0
  ODBSET "AUXCrate/AUX-13/FrontendPzc[15]", n, 1
  ENDODBSUBDIR
  2079   25 Jan 2021 Thomas LindnerSuggestionmhttpd browser caching
I have a more subtle point about the new ODB key for using an external elog I mentioned in [1].  I was very confused after changing the ODB "External Elog" because mhttpd still wasn't using my external elog URL.  I started trying to debug mhttpd.cxx, but found a lot of bits of mhttpd didn't seem to be getting called.  I eventually realized that my browser had been caching the responses for some (though not all) of the MIDAS navigation buttons.  Clearing my browser cache fixed the problem and allowed me to use the MIDAS button to the external ELOG.  This caching happens on my macbook for both Firefox 84.0.2 and Safari 13.1.

Many of the requests to mhttpd end up going to send_fp(), where we explicitly set the cache time to 24 hours.

   // send HTTP cache control headers                                                                                               
   time_t now = time(NULL);
   now += (int) (3600 * 24);
   struct tm* gmt = gmtime(&now);
   const char* format = "%A, %d-%b-%y %H:%M:%S GMT";
   char str[256];
   strftime(str, sizeof(str), format, gmt);
   r->rsprintf("Expires: %s\r\n", str);

Some other MIDAS buttons don't seem to be cached by the browser; for instance the response for the 'OldHistory' button doesn't get cached.

Should we remove the cache instruction for at least some of the buttons?  At least for the elog button where we want the link direction to get switched by an ODB key the caching seems a bad idea.

[1] https://midas.triumf.ca/elog/Midas/2078
  2080   25 Jan 2021 Stefan RittSuggestionmhttpd browser caching
Let me first explain a bit why caching is there. Once we had the case that someone from 
TRIUMF opened a midas custom page at T2K. It took about one minute (!) to load the page. 

When we looked at it, we found that the custom page pulled about 100 items with individual
HTTP requests from Japan, each taking about one second for the roundtrip. Then we redesigned
the custom page communication so that many ODB entries could be retrieved in one operation,
which improved the loading time from 100s to about 2s.

With the buttons we will have to make the same compromise. If we do not cache anything,
loading the midas status page over the Pacific takes many seconds. If we cache all, any
change on the midas side will not be reflected on the web page. So there is a compromise
to be made. I thought I designed it such that the side menu is cached locally, but when
the user presses "reload", then the full menu is fetched from the server. Of course one
has to remember this, so changing the ELOG URL or other things on the menu require a
reload (or wait a certain time for the cache to expire). So try again if that's working
for you. If not, I can visit it again and check if there is any bug.

If we go the route to disable the cache, better try this to T2K and see what you get before
we commit ourselves to that. Last time TRIUMF people were complaining a lot about long
load times.

Best,
Stefan
  2081   25 Jan 2021 Thomas LindnerSuggestionmhttpd browser caching
I tried reloading the pages.  If I reloaded the actual elog page 

https://server.triumf.ca/?cmd=Elog

then it bypassed the cache and got the correct updated page from mhttpd.

However, if when I reloaded the status page

https://server.triumf.ca/?cmd=Status

and then clicked the Elog button then I just got the cached (old) page.  Admittedly reloading the status page doesn't make so much sense (once I thought about it), but it is what I tried first (I'm good at modelling unexpected user behaviour); so there is some risk that the user will try reloading the wrong page and will be stuck not getting the external elog page (until 24 hours runs out).

Anyway, I will update the documentation to note that you need to reload the elog page after changing this variable.  That's probably an adequate solution.

I certainly don't suggest getting rid of caching entirely.  I was trying to think whether there was a set of pages where it would make sense to disable the cache (like the elog page).  But maybe that will just cause more problems.


> Let me first explain a bit why caching is there. Once we had the case that someone from 
> TRIUMF opened a midas custom page at T2K. It took about one minute (!) to load the page. 
> 
> When we looked at it, we found that the custom page pulled about 100 items with individual
> HTTP requests from Japan, each taking about one second for the roundtrip. Then we redesigned
> the custom page communication so that many ODB entries could be retrieved in one operation,
> which improved the loading time from 100s to about 2s.
> 
> With the buttons we will have to make the same compromise. If we do not cache anything,
> loading the midas status page over the Pacific takes many seconds. If we cache all, any
> change on the midas side will not be reflected on the web page. So there is a compromise
> to be made. I thought I designed it such that the side menu is cached locally, but when
> the user presses "reload", then the full menu is fetched from the server. Of course one
> has to remember this, so changing the ELOG URL or other things on the menu require a
> reload (or wait a certain time for the cache to expire). So try again if that's working
> for you. If not, I can visit it again and check if there is any bug.
> 
> If we go the route to disable the cache, better try this to T2K and see what you get before
> we commit ourselves to that. Last time TRIUMF people were complaining a lot about long
> load times.
> 
> Best,
> Stefan
  2085   08 Feb 2021 Konstantin OlchanskiSuggestionmhttpd browser caching
>    r->rsprintf("Expires: %s\r\n", str);

The best I can tell, none of this works in current browsers. with google-chrome,
I see it cache pretty much everything regardless of "expires", "no cache", etc
and anything else I tried.

Things like shift-<reload>, etc used to work to refresh the cache, but not any more.

So, I too, see confusing side-effects of caching, where I change something in ODB,
but "nothing happens". Then I scratch my head for 30 minutes until I remember
to open the javascript debugger where shift-<reload> (or is it ctrl-<reload>) actually works.

It seems that the only reliable way to bypass the browser cache is to add
a tag with a random number to the URL ("&ts=currenttime").

This is for HTTP GET requests. HTTP POST does not seem to be cached, so I do not worry
about this nonsense for json-rpc requests.

Perhaps we should do this random number trick for all user actions. User can
press buttons only so fast, we should be able to sustain the rate. Anything
loaded automatically or from a timer, we should allow caching.

BTW, things like midas.js are also cached, and it is common to see problems
after updating midas, where status.html is newly loaded, but midas.js is an old
stale version from cache.

Messy.

K.O.
  2086   08 Feb 2021 Stefan RittSuggestionmhttpd browser caching
> It seems that the only reliable way to bypass the browser cache is to add
> a tag with a random number to the URL ("&ts=currenttime").

Indeed that's the only reliable way to avoid caching across browsers. An alternative is

("&r=" + Math.random())

to add a random number.


> BTW, things like midas.js are also cached, and it is common to see problems
> after updating midas, where status.html is newly loaded, but midas.js is an old
> stale version from cache.

Reloading JavaScript file NOT from the cache is really tricky these days. I added a
special Google Chrome extension to clear my browser cache, which works reliably:

https://chrome.google.com/webstore/detail/clear-cache/cppjkneekbjaeellbfkmgnhonkkjfpdn

Stefan
  2128   10 Mar 2021 Zaher SalmanSuggestionembed modbvalue in SVG
Is it possible to embed modbvalue in an SVG for use within a custom page?

thanks.
  2129   10 Mar 2021 Stefan RittSuggestionembed modbvalue in SVG
You can't really embed it, but you can overlay it. You tag the SVG with a 
"relative" position and then move the modbvalue with an "absolute" position over 
it:

<svg style="position:relative" width="400" height="100">
  <rect width="300" height="100" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  <div class="modbvalue" style="position:absolute;top:50px;left:50px" data-odb-path="/Runinfo/Run number"></div>
</svg>
  2144   09 Apr 2021 Lars MartinSuggestionTime zone selection for web page
The new history as well as the clock in the web page header show the local time 
of the user's computer running the browser.
Would it be possible to make it either always use the time zone of the Midas 
server, or make it selectable from the config page?
It's not ideal trying to relate error messages from the midas.log to history 
plots if the time stamps don't match.
ELOG V3.1.4-2e1708b5