Back Midas Rome Roody Rootana
  Midas DAQ System, Page 119 of 137  Not logged in ELOG logo
New entries since:Wed Dec 31 16:00:00 1969
ID Date Authorup Topic Subject
  2036   27 Nov 2020 Stefan RittInfoEquipment "common" settings in ODB
Ok, so what about the following proposal:

- I change back the mfe.cxx code to behave like before (ODB has precedence and does not get overwritten when the 
front-end restarts)

- I add a global flag

BOOL equipment_common_overwrite;

and pre-set it to FALSE;

- So if nothing is changed the flag stays false and ODB keeps precedence

- If a frontend wants to overwrite equipment/common on each start, the user sets

BOOL equipment_common_overwrite = TRUE;

near the equipment[] structure in the front-end code. 

- If the flag is true, the mfe.cxx init code copies the equipment[] structure to the ODB on each frontend start

I believe this way we can keep backward compatibility, and add the new way with minimal effort. The only downside 
is that all frontends on this plane have to add at least "BOOL equipment_common_overwrite = FALSE;" in their 
code.

I know global variables are evil, but this way the user can just add the line above to the equipment[] array, so 
one sees this when one edits the equipment[] array, giving motivation to change as needed. So the code would be



BOOL equipment_common_overwrite = TRUE;

EQUIPMENT equipment[] = {
 ....
}



An alternative way would be to add a function

  set_equipment_common_overwrite(TRUE);

into the frontend_init() code. That's somehow cleaner (still needs an internal global variable), but it has to go 
into frontend_init() so won't be at the same place as the EQUIPMENT list in the frontend.

Thoughts?

Best,
Stefan
  2039   30 Nov 2020 Stefan RittInfoEquipment "common" settings in ODB
Ok, I implemented it the following way:

- Added a boolean flag "equipment_common_overwrite", which must be contained in EACH frontend, preferably just 
before the EQUIPMENT structure, such as:

BOOL equipment_common_overwrite = TRUE;

EQUIPMENT equipment[] = {
...
};

- If that flag is TRUE, then the contents of the "equipment" structure is copied to the ODB on each start of the 
front-end

- If the flag is FALSE, then the ODB values are kept on the start of the front-end

The setting of the flag depends now on the philosophy of the experiment. Some experiments say that everything 
needed should be in the front-end code, so when it starts everything gets set correctly. They don't change the 
values in the ODB, but in the frontend code, which then goes into their repository. Other experiments just need 
some default values from the frontend code, and the fine-tune things by changing values in the ODB. These 
experiments should set this flag to FALSE.

*****

Please note that EVERY frontend now needs this flag, so all of you have to add it to all of your front-ends, 
otherwise the front-end will not compile! I could not figure out how to this could be done without this 
requirement, since you can define a global variable only once.

*****


Stefan
  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
  2041   30 Nov 2020 Stefan RittInfoEquipment "common" settings in ODB
One more change: 

After using the new code for some hours, we realized that the "enabled" flag should not come from the frontend code, 
but always be defined by the ODB. So if you quickly have to disable some equipment because the associated hardware is 
off, you want to change this flag only in the ODB and not have to recompile the frontend. So we exclude that flag from 
being set by the frontend. It is anyhow special, because one sees all disable equipment in the main midas status page, 
so one knows what's on and what's off.

Please comment here if you think that change causes problem. Anyhow it's working now for the enabled flag as before 
all these changes.

Stefan
  2046   01 Dec 2020 Stefan RittForumsubrun
There is no "mechanism" foreseen to be executed after each subrun. But you could 
run a shell script after each run which loops over all subruns and converts them 
one after the other.

Stefan

> Hi,
> 
> I was wondering if there is a "mechanism" to run an executable
> file after each subrun is closed...
> 
> I need to convert .mid.lz4 subrun files to ROOT (TTree) files;
> 
> Thanks,
> Gennaro
  2050   09 Dec 2020 Stefan RittForumhistory and variables confusion
First, the writing of banks is completely independent of the history system. Banks go to the log file only, 
while the history is only linked to the "Variables" section in the ODB.

Second, it's advisable to group similar equipment into one. Like if you have five power supplies powering
and experiment, you don't want to have five equipments Supply1, Supply2, ..., but only one equipment
"Power Supplies". In the frontend belonging to that equipment, you define a DEVICE_DRIVER list with
one entry for each power supply. If you interact with an mscb device, there are some helper functions
which simplify the definition of the equipment and which I can send you privately. So your device
driver looks a bit like the one attached.

If you cannot do that and absolutely want separate equipments, please post a complete ODB subtree of your
settings, and I can try to reproduce your problem.

Stefan

======================

DEVICE_DRIVER power_driver[] = {
   {"Power Supply 1", mscbdev, 0, NULL, DF_INPUT | DF_MULTITHREAD},
   {"Power Supply 2", mscbdev, 0, NULL, DF_INPUT | DF_MULTITHREAD},
   {"Power Supply 3", mscbdev, 0, NULL, DF_INPUT | DF_MULTITHREAD},
   {""}
};

...

INT frontend_init()
{
   mscb_define("mscbxxx.psi.ch", "Power Supplies", "Power Supply 1", power_driver, 1, 0, "Output 1", 0.1);
   mscb_define("mscbxxx.psi.ch", "Power Supplies", "Power Supply 1", power_driver, 1, 1, "Output 2", 0.1);
   ...
}

/*-- Function to define MSCB variables in a convenient way ---------*/

void mscb_define(const char *submaster, const char *equipment, const char *devname, 
                 DEVICE_DRIVER *driver, int address, unsigned char var_index, 
                 const char *name, double threshold)
{
   int i, dev_index, chn_index, chn_total;
   char str[256];
   float f_threshold;
   HNDLE hDB;

   cm_get_experiment_database(&hDB, NULL);

   if (submaster && submaster[0]) {
      sprintf(str, "/Equipment/%s/Settings/Devices/%s/Device", equipment, devname);
      db_set_value(hDB, 0, str, submaster, 32, 1, TID_STRING);
      sprintf(str, "/Equipment/%s/Settings/Devices/%s/Pwd", equipment, devname);
      db_set_value(hDB, 0, str, "meg", 32, 1, TID_STRING);
   }

   /* find device in device driver */
   for (dev_index=0 ; driver[dev_index].name[0] ; dev_index++)
      if (equal_ustring(driver[dev_index].name, devname))
         break;

   if (!driver[dev_index].name[0]) {
      cm_msg(MERROR, "mscb_define", "Device \"%s\" not present in device driver list", devname);
      return;
   }

   /* count total number of channels */
   for (i=chn_total=0 ; i<=dev_index ; i++)
      if (((driver[dev_index].flags & DF_INPUT) > 0 && (driver[i].flags & DF_INPUT)) ||
          ((driver[dev_index].flags & DF_OUTPUT) > 0 && (driver[i].flags & DF_OUTPUT)))
      chn_total += driver[i].channels;

   chn_index = driver[dev_index].channels;
   sprintf(str, "/Equipment/%s/Settings/Devices/%s/MSCB Address", equipment, devname);
   db_set_value_index(hDB, 0, str, &address, sizeof(int), chn_index, TID_INT, TRUE);
   sprintf(str, "/Equipment/%s/Settings/Devices/%s/MSCB Index", equipment, devname);
   db_set_value_index(hDB, 0, str, &var_index, sizeof(char), chn_index, TID_BYTE, TRUE);

   if (threshold != -1 && (driver[dev_index].flags & DF_INPUT) > 0) {
     sprintf(str, "/Equipment/%s/Settings/Update Threshold", equipment);
     f_threshold = (float) threshold;
     db_set_value_index(hDB, 0, str, &f_threshold, sizeof(float), chn_total, TID_FLOAT, TRUE);
   }

   if (name && name[0]) {
      sprintf(str, "/Equipment/%s/Settings/Names %s", equipment, devname);
      db_set_value_index(hDB, 0, str, name, 32, chn_total, TID_STRING, TRUE);
   }

   /* increment number of channels for this driver */
   driver[dev_index].channels++;
}
  2059   16 Dec 2020 Stefan RittForumIssues building banks.
> This is very hard to do using the mfe.c frontend. (the main reason I wrote the TMFE C++ frontend class).

Actually that's not true. Just look at 

midas/examples/mtfe/mtfe.c

this is an example for a frontend with equipment with the EQ_USER flag, which allows you easily to run a separate 
thread (or more) for event collection and processing. Of course all old-fashioned C style (code is from 2007) but it 
works.

Stefan
  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
  2066   06 Jan 2021 Stefan RittBug ReportLogger: Disk nearly full.
The logger simple requests the disk free space level from the operating system in the same 
way as the "df" command does. Can you do a "df" on your system? I have seen that some file 
systems free up space not immediately if you delete files, but some times later (like 24h).

Stefan
  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
  2070   08 Jan 2021 Stefan RittForumhistory and variables confusion
We kind of agreed to rewrite the slow control system in C++. Each device will have its own driver derived from a common base class implementing the general communication. The reason we need a "system" and not only a "hand-written" driver is because we want:

- glue many device drivers together for a single equipment
- have a dedicated readout thread for every device, in order not to block other devices
- have a common error reporting scheme working with several threads
- being able to disable/enable individual devices without changing the history system each time
- having a common naming scheme for all devices (like "enforce" /Equipment/<name>/Settings/Names xxx) which is needed by the history system
- ...

Will see when we have time for that.

Stefan
  2074   13 Jan 2021 Stefan RittForumpoll_event() is very slow.
Something must be wrong on your side. If you take the example frontend under

midas/examples/experiment/frontend.cxx

and let it run to produce dummy events, you get about 90 Hz. This is because we have a

  ss_sleep(10);

in the read_trigger_event() routine to throttle things down. If you remove that sleep, 
you get an event rate of about 500'000 Hz. So the framework is really quick.

Probably your routine which looks for a 'lam' takes really long and should be fixed.

Stefan
  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
  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
  2097   25 Feb 2021 Stefan RittBug Reporthistory reload
I have to reproduce the problem. Can you please send me the full link by direct email. As you know, I'm also at PSI.

Stefan
  2116   02 Mar 2021 Stefan RittInfoshortest possible sleep
Why do you need that? Periodic equipment typically runs ever ten seconds or so, meaning one can do this easily in a scheduler.

For polled equipment, you don't want to sleep at all. Because if you sleep, you might miss an event. That's why I put my poll in mfe.c into a for() loop. No 
sleep, maximum polling rate. I just double checked on my macbook air. 

- If poll is always false (no event available), the loop executes 50M times in 100ms (calibrated during startup of the frontend). That means one iteration 
takes 2ns (!). So if an event occurs, the readout is started with a 2ns overhead. No sleep can beat that. In a real world application, one has to add of course 
the VME access or so to poll for the event.

- If poll is always true, the framework generates about 700k events each second (returning jus a few bytes of event data).

So if one adds any sleep here, things can get only worse, so I don't see the point for that. Of course polling eats one kernel at 100%, but these days every 
CPU has more than one, even my 800 MHz Xilinx embedded ARM CPU (Zynq).

Best,
Stefan
  Draft   04 Mar 2021 Stefan RittForumUsing JSROOT.openFile with Midas
I also need midas events going back to the
  2122   04 Mar 2021 Stefan RittForumUsing JSROOT.openFile with Midas
I also need midas events going back to the browser for single event display, so put +1 for me.

Please also consider to use JavaScript typed arrays instead of JSON. For large midas banks, type 
arrays are 5-10 times faster than JSON encoding/decoding.

Best,
Stefan
  2127   10 Mar 2021 Stefan RittForumINT INT32 in experim.h
Ok, I added

/* define integer types with explicit widths */
#ifndef NO_INT_TYPES_DEFINE
typedef unsigned char      UINT8;
typedef char               INT8;
typedef unsigned short     UINT16;
typedef short              INT16;
typedef unsigned int       UINT32;
typedef int                INT32;
typedef unsigned long long UINT64;
typedef long long          INT64;
#endif

to cover all new types. If there is a collision with user defined types, compile your program with -DNO_INT_TYPES_DEFINE and you remove the 
above definition. I hope there are no other conflicts.

Stefan
ELOG V3.1.4-2e1708b5