From 3ebc3eb523f527ac42f9224e89dde84e2c77613c Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Tue, 27 Feb 2024 03:30:50 +0100
Subject: [PATCH 01/24] WIP: UpdateAndReset

---
 MooreOnlineConf/options/online.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/MooreOnlineConf/options/online.py b/MooreOnlineConf/options/online.py
index 0ddbe8b1d..6f2682e84 100644
--- a/MooreOnlineConf/options/online.py
+++ b/MooreOnlineConf/options/online.py
@@ -69,12 +69,6 @@ elif task_type != "HLT2" and task_type != "CalibMon":
     # intermediate saveset period in seconds
     application.updateAndReset.saverCycle = 600
 else:
-    # TODO AdditionalAlgs / PreambleAlgs should be used for all tasks...
-    from MooreOnlineConf.utils import update_and_reset
-    uar_config = update_and_reset().configuration()
-    uar_algs = uar_config.apply()[0]
-    HiveDataBrokerSvc().DataProducers.extend(uar_algs)
-    flow.AdditionalAlgs = uar_algs
     application.updateAndReset.saveSetFilePrefix = (
         "ByRun/${TASKNAME}/ToMerge/" +
         "${RUN10000}/${RUN1000}/${RUN}/${UTGID}-${RUN}-${TIME}")
@@ -82,6 +76,14 @@ else:
     application.updateAndReset.saverCycle = 3600 * 24 * 7
     application.updateAndReset.publishSaveSetLocation = False
 
+if "UpdateAndReset" not in allConfigurables:
+    # TODO we should remove update_and_reset from task python options
+    from MooreOnlineConf.utils import update_and_reset
+    uar_config = update_and_reset().configuration()
+    uar_algs = uar_config.apply()[0]
+    HiveDataBrokerSvc().DataProducers.extend(uar_algs)
+    flow.AdditionalAlgs = uar_algs
+
 if OnlineEnv.PartitionName.startswith("TEST"):
     application.updateAndReset.saveSetDir = "Savesets"
     # application.updateAndReset.saverCycle = 20
-- 
GitLab


From a613ce652918562a75636669836d3f529fbdd164 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 12 Jul 2023 17:32:53 +0200
Subject: [PATCH 02/24] Do not patch run numbers

---
 MooreScripts/options/MDFProd.opts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MooreScripts/options/MDFProd.opts b/MooreScripts/options/MDFProd.opts
index 66aacc5c5..0db980a27 100644
--- a/MooreScripts/options/MDFProd.opts
+++ b/MooreScripts/options/MDFProd.opts
@@ -32,7 +32,7 @@ Reader.MMapFiles            = 0;
 Reader.ReuseFile            = 0;
 Reader.PackingFactor        = 20;
 Reader.AllocationSizekB     = 2000;
-Reader.PatchOdin            = 5000000;
+Reader.PatchOdin            = 0;
 //
 MEPManager.PartitionBuffers = true;
 MEPManager.PartitionName    = @OnlineEnv.PartitionName;
-- 
GitLab


From e7688e90883d9d338178c11677c1326cc9aeaae8 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 12 Jul 2023 17:33:23 +0200
Subject: [PATCH 03/24] Monitor saveset path dim service

---
 .../python/MooreScripts/testbench/emulator.py       |  3 +++
 .../MooreScripts/testbench/scenarios/alignment.py   |  4 ++--
 .../MooreScripts/testbench/scenarios/default.py     | 13 ++++++++++++-
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/MooreScripts/python/MooreScripts/testbench/emulator.py b/MooreScripts/python/MooreScripts/testbench/emulator.py
index 255f3cd5d..8e0b25d40 100644
--- a/MooreScripts/python/MooreScripts/testbench/emulator.py
+++ b/MooreScripts/python/MooreScripts/testbench/emulator.py
@@ -214,6 +214,9 @@ class Task:
         self._status_task = None
         self.process = None
         self.utgid = args["args"][0]
+        for a in args["args"]:
+            if a.startswith("-type="):
+                self.type = a.removeprefix("-type=")
 
     async def __aenter__(self):
         pass
diff --git a/MooreScripts/python/MooreScripts/testbench/scenarios/alignment.py b/MooreScripts/python/MooreScripts/testbench/scenarios/alignment.py
index b47475e80..53bd6f234 100644
--- a/MooreScripts/python/MooreScripts/testbench/scenarios/alignment.py
+++ b/MooreScripts/python/MooreScripts/testbench/scenarios/alignment.py
@@ -23,8 +23,8 @@ log = logging.getLogger(__name__)
 
 
 async def run(tasks, args):
-    analyzers = [t for t in tasks if "Wrk" in t.utgid]
-    iterator, = [t for t in tasks if "Drv" in t.utgid]
+    analyzers = [t for t in tasks if "Wrk" in t.type]
+    iterator, = [t for t in tasks if "Drv" in t.type]
     assert len(tasks) == len(analyzers) + 1
 
     # Write the alignment-specific options
diff --git a/MooreScripts/python/MooreScripts/testbench/scenarios/default.py b/MooreScripts/python/MooreScripts/testbench/scenarios/default.py
index cec8142d4..d8c659518 100644
--- a/MooreScripts/python/MooreScripts/testbench/scenarios/default.py
+++ b/MooreScripts/python/MooreScripts/testbench/scenarios/default.py
@@ -10,6 +10,7 @@
 ###############################################################################
 import asyncio
 import logging
+from MooreScripts.testbench import emulator, asyncdim
 from MooreScripts.testbench.emulator import (
     tasks_load,
     tasks_wait_for_status,
@@ -18,11 +19,12 @@ from MooreScripts.testbench.emulator import (
     tasks_measure_throughput,
     tasks_wait_for_output,
 )
+from typing import List
 
 log = logging.getLogger(__name__)
 
 
-async def run(tasks, args):
+async def run(tasks: List[emulator.Task], args):
     await tasks_load(tasks)
     # TODO for some reason HLT2 publishes OFFLINE before NOT_READY, but only sometimes
     await tasks_wait_for_status(tasks, "NOT_READY", skip=["OFFLINE"])
@@ -30,6 +32,15 @@ async def run(tasks, args):
     await tasks_send_command(tasks, "configure")
     await tasks_wait_for_status(tasks, "READY")
 
+    monitors = [t for t in tasks if t.type.endswith("Mon")]
+    if monitors:
+        if len(monitors) > 1:
+            raise ValueError("Too many *Mon tasks in architecture")
+        monitor = monitors[0]
+        asyncdim.DimService(f"{args.partition}/{monitor.type}/SAVESETLOCATION",
+                            "C").__enter__()
+        # TODO this needs to run in a dedicated task and have proper cleanup
+
     await tasks_send_command(tasks, "start")
     await tasks_wait_for_status(tasks, "RUNNING")
 
-- 
GitLab


From e08064fe7fd25caa179750e79d923691f57191e7 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 12 Jul 2023 17:33:50 +0200
Subject: [PATCH 04/24] WIP: changes only useful for testing?

---
 MooreOnlineConf/options/online.py | 3 ++-
 MooreScripts/options/MDFProd.opts | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/MooreOnlineConf/options/online.py b/MooreOnlineConf/options/online.py
index 6f2682e84..40f7bb831 100644
--- a/MooreOnlineConf/options/online.py
+++ b/MooreOnlineConf/options/online.py
@@ -67,7 +67,8 @@ if task_type == "RecoMon":
     application.updateAndReset.saverCycle = 300
 elif task_type != "HLT2" and task_type != "CalibMon":
     # intermediate saveset period in seconds
-    application.updateAndReset.saverCycle = 600
+    application.updateAndReset.saverCycle = 10
+    application.updateAndReset.desiredDeltaTCycle = 10
 else:
     application.updateAndReset.saveSetFilePrefix = (
         "ByRun/${TASKNAME}/ToMerge/" +
diff --git a/MooreScripts/options/MDFProd.opts b/MooreScripts/options/MDFProd.opts
index 0db980a27..5d4101b16 100644
--- a/MooreScripts/options/MDFProd.opts
+++ b/MooreScripts/options/MDFProd.opts
@@ -19,7 +19,7 @@ Reader.BrokenHosts          = "";
 Reader.Directories          = @OnlineEnv.Reader_Directories;
 Reader.FilePrefix           = @OnlineEnv.Reader_FilePrefix;
 Reader.AllowedRuns          = {"*"};
-Reader.MuDelay              = 0;
+Reader.MuDelay              = 500000;  // 25 ms * 20 (packing factor)
 Reader.DeleteFiles          = false;
 Reader.SaveRest             = false;
 Reader.PauseSleep           = 2;  // Optional wait time until 'Output' event queue is empty
-- 
GitLab


From cbc7bfca75de79e09d98f7b27354710974cda96a Mon Sep 17 00:00:00 2001
From: Hlt Oper <hlt_oper@cern.ch>
Date: Tue, 3 Oct 2023 15:41:55 +0200
Subject: [PATCH 05/24] Fix online.py debugging

---
 MooreOnlineConf/options/online.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/MooreOnlineConf/options/online.py b/MooreOnlineConf/options/online.py
index 40f7bb831..6f2682e84 100644
--- a/MooreOnlineConf/options/online.py
+++ b/MooreOnlineConf/options/online.py
@@ -67,8 +67,7 @@ if task_type == "RecoMon":
     application.updateAndReset.saverCycle = 300
 elif task_type != "HLT2" and task_type != "CalibMon":
     # intermediate saveset period in seconds
-    application.updateAndReset.saverCycle = 10
-    application.updateAndReset.desiredDeltaTCycle = 10
+    application.updateAndReset.saverCycle = 600
 else:
     application.updateAndReset.saveSetFilePrefix = (
         "ByRun/${TASKNAME}/ToMerge/" +
-- 
GitLab


From cc211c30890cf98945f27d0e76828757ae63e8f9 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 4 Oct 2023 12:39:13 +0200
Subject: [PATCH 06/24] Undo MuDelay

---
 MooreScripts/options/MDFProd.opts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MooreScripts/options/MDFProd.opts b/MooreScripts/options/MDFProd.opts
index 5d4101b16..0db980a27 100644
--- a/MooreScripts/options/MDFProd.opts
+++ b/MooreScripts/options/MDFProd.opts
@@ -19,7 +19,7 @@ Reader.BrokenHosts          = "";
 Reader.Directories          = @OnlineEnv.Reader_Directories;
 Reader.FilePrefix           = @OnlineEnv.Reader_FilePrefix;
 Reader.AllowedRuns          = {"*"};
-Reader.MuDelay              = 500000;  // 25 ms * 20 (packing factor)
+Reader.MuDelay              = 0;
 Reader.DeleteFiles          = false;
 Reader.SaveRest             = false;
 Reader.PauseSleep           = 2;  // Optional wait time until 'Output' event queue is empty
-- 
GitLab


From 3b4cbf68b3eebd6e5d1092b03784cb9da1bff93a Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Mon, 9 Jan 2023 17:04:58 +0100
Subject: [PATCH 07/24] Switch to LHCb scheduler

---
 MooreOnlineConf/options/online.py | 40 ++++++++++++++-----------------
 1 file changed, 18 insertions(+), 22 deletions(-)

diff --git a/MooreOnlineConf/options/online.py b/MooreOnlineConf/options/online.py
index 6f2682e84..46461d4a6 100644
--- a/MooreOnlineConf/options/online.py
+++ b/MooreOnlineConf/options/online.py
@@ -11,10 +11,13 @@
 import os
 import GaudiOnline
 import OnlineEnvBase as OnlineEnv
-from Configurables import (Online__AlgFlowManager as AlgFlowManager,
-                           Online__OutputAlg as OutputAlg)
-from Configurables import (HLTControlFlowMgr, ExecutionReportsWriter,
-                           ApplicationMgr, OnlMonitorSink, HiveDataBrokerSvc)
+from Configurables import (
+    Online__OutputAlg as OutputAlg,
+    ApplicationMgr,
+    OnlMonitorSink,
+    HiveDataBrokerSvc,
+    HLTControlFlowMgr,
+)
 from Gaudi.Configuration import allConfigurables
 
 task_type = os.getenv("TASK_TYPE", "GenericTask")
@@ -34,25 +37,11 @@ for name, configurable in allConfigurables.items():
         writer.MBM_maxConsumerWait = 10
         writer.MBM_allocationSize = 400 * 1024
 
-flow = AlgFlowManager("EventLoop")
-application.app.EventLoop = flow
-
 ApplicationMgr().ExtSvc.append(
     OnlMonitorSink(
         CountersToPublish=[("Combiner", "# passed"), ("Prescaler", "#accept")],
         HistogramsToPublish=[(".*", ".*")]))
 
-# input = application.setup_event_input(None)
-
-# HACK: transfer options from HLTControlFlowMgr
-cfm = HLTControlFlowMgr('HLTControlFlowMgr')
-flow.CompositeCFNodes = cfm.CompositeCFNodes
-flow.BarrierAlgNames = cfm.BarrierAlgNames
-
-# HACK: tell the HltDecReports creator to use the online scheduler
-# only works because there is exactly one instance of ExecutionReportsWriter
-ExecutionReportsWriter().Scheduler = flow
-
 application.setup_monitoring(task_type)
 application.updateAndReset.saveHistograms = 1
 application.updateAndReset.saveSetDir = "/hist/Savesets"
@@ -82,7 +71,7 @@ if "UpdateAndReset" not in allConfigurables:
     uar_config = update_and_reset().configuration()
     uar_algs = uar_config.apply()[0]
     HiveDataBrokerSvc().DataProducers.extend(uar_algs)
-    flow.AdditionalAlgs = uar_algs
+    HLTControlFlowMgr('HLTControlFlowMgr').PreambleAlgs = uar_algs
 
 if OnlineEnv.PartitionName.startswith("TEST"):
     application.updateAndReset.saveSetDir = "Savesets"
@@ -94,12 +83,19 @@ try:
 except (KeyError, ValueError):
     n_threads = 2 if task_type == "HLT2" else 1
 
-application.config.numThreadSvcName = 'NumThreads'
-
 # Use execMode = 1 for multi-threaded (async_queued) mode and
 # use 0 (default) for single-threaded (sync) mode, i.e. debugging.
-application.config.execMode = 1 if n_threads > 1 else 0
+application.config.execMode = 1
 application.config.numEventThreads = n_threads
+# Enable controlling number of threads with a DIM command
+application.config.numThreadSvcName = 'NumThreads'
+
+event_store = allConfigurables["EventDataSvc"]
+# Ensure enough event slots to "bridge" the short wait when
+# switching event bursts.
+event_store.EventSlots = max(
+    event_store.getProp("EventSlots"),
+    n_threads + max(int(0.25 * n_threads), 1))
 
 if task_type == "HLT2":
     # for HLT2 we must use UserType=ONE, i.e. every event must be seen
-- 
GitLab


From 779d5a4aa6843080008f7aeb2ccbe53b27651b91 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Tue, 27 Feb 2024 03:24:03 +0100
Subject: [PATCH 08/24] Fix UpdateAndReset and enable SuperTAE

---
 MooreOnlineConf/options/online.py | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/MooreOnlineConf/options/online.py b/MooreOnlineConf/options/online.py
index 46461d4a6..8ec260b45 100644
--- a/MooreOnlineConf/options/online.py
+++ b/MooreOnlineConf/options/online.py
@@ -65,13 +65,12 @@ else:
     application.updateAndReset.saverCycle = 3600 * 24 * 7
     application.updateAndReset.publishSaveSetLocation = False
 
-if "UpdateAndReset" not in allConfigurables:
-    # TODO we should remove update_and_reset from task python options
-    from MooreOnlineConf.utils import update_and_reset
-    uar_config = update_and_reset().configuration()
-    uar_algs = uar_config.apply()[0]
-    HiveDataBrokerSvc().DataProducers.extend(uar_algs)
-    HLTControlFlowMgr('HLTControlFlowMgr').PreambleAlgs = uar_algs
+# TODO we should remove update_and_reset from task python options
+from MooreOnlineConf.utils import update_and_reset
+uar_config = update_and_reset().configuration()
+uar_algs = uar_config.apply()[0]
+HiveDataBrokerSvc().DataProducers.extend(uar_algs)
+HLTControlFlowMgr('HLTControlFlowMgr').PreambleAlgs = uar_algs
 
 if OnlineEnv.PartitionName.startswith("TEST"):
     application.updateAndReset.saveSetDir = "Savesets"
@@ -137,3 +136,5 @@ if task_type != "HLT2":
     # Speed up monitoring tasks by not loading the full geometry
     from Configurables import LHCb__DetDesc__ReserveDetDescForEvent as reserveIOV
     reserveIOV("reserveIOV").PreloadGeometry = False
+
+application.config.expandTAE = True
-- 
GitLab


From eef9a8873d60a0c85ed8cc50d6d0c5e676f2cc24 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Tue, 27 Feb 2024 03:25:07 +0100
Subject: [PATCH 09/24] Modernize PlumeMon config and add TAE monitor

---
 MooreOnlineConf/options/odin.py               |  30 +++++
 MooreOnlineConf/options/plume.py              |  74 ++++++++----
 .../python/MooreOnlineConf/utils.py           | 105 +++++++++++++++++-
 3 files changed, 188 insertions(+), 21 deletions(-)
 create mode 100644 MooreOnlineConf/options/odin.py

diff --git a/MooreOnlineConf/options/odin.py b/MooreOnlineConf/options/odin.py
new file mode 100644
index 000000000..e2ec02297
--- /dev/null
+++ b/MooreOnlineConf/options/odin.py
@@ -0,0 +1,30 @@
+###############################################################################
+# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration           #
+#                                                                             #
+# This software is distributed under the terms of the GNU General Public      #
+# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
+#                                                                             #
+# In applying this licence, CERN does not waive the privileges and immunities #
+# granted to it by virtue of its status as an Intergovernmental Organization  #
+# or submit itself to any jurisdiction.                                       #
+#############################################################################
+from PyConf.application import make_odin
+from Moore import options, run_reconstruction
+from Moore.config import Reconstruction
+from PyConf.Algorithms import ODINMonitor
+
+
+def main():
+    odin = make_odin()
+    moni = ODINMonitor(name="ODIN", Input=odin)
+
+    algs = []
+    if options.input_type.lower() == 'online':
+        from MooreOnlineConf.utils import update_and_reset
+        algs.append(update_and_reset())
+    algs.append(moni)
+
+    return Reconstruction('plume_moni', algs, filters=[])
+
+
+run_reconstruction(options, main)
diff --git a/MooreOnlineConf/options/plume.py b/MooreOnlineConf/options/plume.py
index 526d525ff..1da288faa 100644
--- a/MooreOnlineConf/options/plume.py
+++ b/MooreOnlineConf/options/plume.py
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration           #
+# (c) Copyright 2000-2024 CERN for the benefit of the LHCb Collaboration      #
 #                                                                             #
 # This software is distributed under the terms of the GNU General Public      #
 # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
@@ -7,29 +7,63 @@
 # In applying this licence, CERN does not waive the privileges and immunities #
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
-#############################################################################
-from PyConf.application import default_raw_banks, make_odin
-from Moore import options, run_reconstruction
-from PyConf.Algorithms import PlumeRawToDigits
-from Moore.config import Reconstruction
-from RecoConf.standalone import reco_prefilters
-from PyConf.Algorithms import PlumeDigitMonitor
+###############################################################################
+from PyConf.application import (
+    configure_input,
+    configure,
+    default_raw_banks,
+    make_odin,
+)
+from PyConf.Algorithms import (
+    PlumeRawToDigits,
+    PlumeDigitMonitor,
+    PlumeTAEMonitor,
+)
+from MooreOnlineConf.utils import (
+    common_monitors_node,
+    passes_rb,
+    RoutingBit,
+    is_tae,
+    decode_tae,
+    if_then,
+    run_all,
+)
+from Moore import options
 
 
-def plume_moni():
+def make_plume_digits(name=""):
+    raw = default_raw_banks("Plume")
     digits = PlumeRawToDigits(
-        name="PlumeRawToDigits",
-        RawBankLocation=default_raw_banks("Plume")).Output
-    odin = make_odin()
-    moni = PlumeDigitMonitor(name="PlumeDigitMonitor", Input=digits, ODIN=odin)
+        name=f"PlumeRawToDigits{name}", RawBanks=raw).Output
+    return digits
+
+
+def main():
+    # the standard monitor
+    monitor = PlumeDigitMonitor(
+        name="PlumeDigitMonitor", Input=make_plume_digits(), ODIN=make_odin())
+
+    # the TAE monitor
+    tae_decoding, tae_odins, tae_data = decode_tae(
+        make_plume_digits, half_window=3)
+    tae_monitor = PlumeTAEMonitor(
+        name="PlumeTAEMonitor",
+        ODINVector=list(tae_odins.values()),
+        InputVector=list(tae_data.values()),
+        is_barrier=True)
 
-    algs = []
-    if options.input_type.lower() == 'online':
-        from MooreOnlineConf.utils import update_and_reset
-        algs.append(update_and_reset())
-    algs.append(moni)
+    # assemble the control flow
+    top_node = run_all(
+        "top",
+        [
+            common_monitors_node(),  # common monitoring to all tasks
+            if_then("IfLUMI", passes_rb(RoutingBit.LUMI), monitor),
+            if_then("IfTAE", is_tae(),
+                    run_all("TAE", [tae_decoding, tae_monitor])),
+        ])
 
-    return Reconstruction('plume_moni', algs, reco_prefilters(gec=False))
+    return top_node
 
 
-run_reconstruction(options, plume_moni)
+configure_input(options)
+configure(options, main())
diff --git a/MooreOnlineConf/python/MooreOnlineConf/utils.py b/MooreOnlineConf/python/MooreOnlineConf/utils.py
index b996a7013..fd0138e08 100644
--- a/MooreOnlineConf/python/MooreOnlineConf/utils.py
+++ b/MooreOnlineConf/python/MooreOnlineConf/utils.py
@@ -14,7 +14,110 @@ import re
 from dataclasses import dataclass
 from datetime import datetime
 from pathlib import Path
-from PyConf.application import default_raw_event
+from enum import IntEnum
+from PyConf.application import default_raw_event, make_odin, default_raw_banks
+from PyConf.Algorithms import TESCheck, OdinTypesFilter
+from PyConf.Algorithms import HltRoutingBitsMonitor, HltRoutingBitsFilter
+from PyConf.control_flow import CompositeNode, NodeLogic
+from Configurables import Online__BanksToRawEvent
+
+
+def run_all(name, children, force_order=False):
+    return CompositeNode(
+        name,
+        children=children,
+        combine_logic=NodeLogic.NONLAZY_OR,
+        force_order=force_order)
+
+
+def and_(name, children, force_order=False):
+    return CompositeNode(
+        name,
+        children=children,
+        combine_logic=NodeLogic.LAZY_AND,
+        force_order=force_order,
+    )
+
+
+def if_then(name, predicate, then):
+    return CompositeNode(
+        name,
+        children=[predicate, then],
+        combine_logic=NodeLogic.LAZY_AND,
+        force_order=True,
+    )
+
+
+def tae_name(offset: int):
+    if not isinstance(offset, int):
+        raise TypeError("offset must be integer")
+    if abs(offset) > 9:
+        raise ValueError(f"Offsets larger than 9 not supported (got {offset})")
+    return "" if offset == 0 else f"{'Prev' if offset < 0 else 'Next'}{abs(offset)}"
+
+
+# def tae_prefix(offset: int):
+#     name = tae_name(offset)
+#     return f"/Event/{name}" if name else "/Event"
+
+
+def common_monitors_node():
+    rb_monitor = HltRoutingBitsMonitor(
+        name="HltRoutingBitsMonitor",
+        RawBanks=default_raw_banks("HltRoutingBits"))
+    # odin_monitor = ODINMonitor(name="ODINMon", Input=make_odin())
+    return run_all("Common", [rb_monitor])
+
+
+class RoutingBit(IntEnum):
+    LUMI = 1
+    PHYSICS = 14
+    SMOG = 15
+
+
+def passes_rb(bit: RoutingBit):
+    if bit > 31:
+        raise NotImplementedError("Filtering on bits above 31 not supported")
+    return HltRoutingBitsFilter(
+        name=f"RBFilterBit{bit:03}",
+        RawBanks=default_raw_banks('HltRoutingBits'),
+        RequireMask=(1 << bit, 0, 0),
+        PassOnError=False)
+
+
+def is_tae():
+    odin = make_odin()
+    return OdinTypesFilter(ODIN=odin, TAEIndexMoreThan=0)
+
+
+def decode_tae(make_data, half_window):
+    children = []
+    odin_bxs = {}
+    data_bxs = {}
+    for offset in range(-half_window, half_window + 1):
+        name = tae_name(offset)
+
+        with default_raw_banks.bind(tae_pos=name):
+            tae_input = default_raw_banks(
+                "ODIN").producer.inputs["RawEventLocation"]
+            if tae_input.producer.type == Online__BanksToRawEvent:
+                tae_input = tae_input.producer.inputs["RawData"]
+
+            check = TESCheck(
+                name=f"TESCheck{name}",
+                Inputs=[tae_input.location],
+                Stop=False,
+                OutputLevel=4)
+            odin_bx = make_odin(name="Decode_ODIN" + name)
+            data_bx = make_data(name=name)
+
+        odin_bxs[offset] = odin_bx
+        data_bxs[offset] = data_bx
+        children.append(
+            and_(f"TAEBX{name}", [check, odin_bx, data_bx], force_order=True))
+
+    cf_node = run_all("DecodeTAE", children)
+    return cf_node, odin_bxs, data_bxs
 
 
 def update_and_reset():
-- 
GitLab


From e5816a588274e2a88e8af6df84a9760f0e0bb596 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Thu, 30 Nov 2023 15:24:56 +0100
Subject: [PATCH 10/24] Add writer to HLT2 architectures

---
 MooreScripts/tests/options/HLT2/Arch.xml     | 13 +++++++++++++
 MooreScripts/tests/options/HLT2Slim/Arch.xml | 13 +++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/MooreScripts/tests/options/HLT2/Arch.xml b/MooreScripts/tests/options/HLT2/Arch.xml
index 26398c47c..cae67b180 100644
--- a/MooreScripts/tests/options/HLT2/Arch.xml
+++ b/MooreScripts/tests/options/HLT2/Arch.xml
@@ -49,4 +49,17 @@
     <timeout action="load" value="20" />
   </task>
 
+  <task name="Writer" user="${USER}" group="${GROUP}">
+    <command>${MOORESCRIPTSROOT}/scripts/runDFTask.sh</command>
+    <argument name="-type" value="${NAME}" />
+    <argument name="-runinfo" value="${RUNINFO}" />
+    <argument name="-options" value="${MOORESCRIPTSROOT}/options/TestWriter.opts" />
+    <argument name="-class" value="Class1" />
+    <fmcparam name="utgid" value="${PARTITION}_${NODE}_${NAME}_${INSTANCE}" />
+    <fmcparam name="define" value="BINARY_TAG=${BINARY_TAG}" />
+    <fmcparam name="define" value="WORKING_DIR=${WORKING_DIR}" />
+    <timeout action="Any" value="20" />
+    <timeout action="load" value="20" />
+  </task>
+
 </tasks_inventory>
diff --git a/MooreScripts/tests/options/HLT2Slim/Arch.xml b/MooreScripts/tests/options/HLT2Slim/Arch.xml
index 449e03c94..4b48a26c6 100644
--- a/MooreScripts/tests/options/HLT2Slim/Arch.xml
+++ b/MooreScripts/tests/options/HLT2Slim/Arch.xml
@@ -47,4 +47,17 @@
     <timeout action="load" value="20" />
   </task>
 
+  <task name="Writer" user="${USER}" group="${GROUP}">
+    <command>${MOORESCRIPTSROOT}/scripts/runDFTask.sh</command>
+    <argument name="-type" value="${NAME}" />
+    <argument name="-runinfo" value="${RUNINFO}" />
+    <argument name="-options" value="${MOORESCRIPTSROOT}/options/TestWriter.opts" />
+    <argument name="-class" value="Class1" />
+    <fmcparam name="utgid" value="${PARTITION}_${NODE}_${NAME}_${INSTANCE}" />
+    <fmcparam name="define" value="BINARY_TAG=${BINARY_TAG}" />
+    <fmcparam name="define" value="WORKING_DIR=${WORKING_DIR}" />
+    <timeout action="Any" value="20" />
+    <timeout action="load" value="20" />
+  </task>
+
 </tasks_inventory>
-- 
GitLab


From a0d1b9cccf97b5a8649a2651b0010f20703e73cd Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Mon, 4 Mar 2024 07:27:02 +0100
Subject: [PATCH 11/24] Increase MBM output buffer size and output burst size

---
 MooreOnlineConf/options/online.py | 2 +-
 MooreScripts/options/MonMBM.opts  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/MooreOnlineConf/options/online.py b/MooreOnlineConf/options/online.py
index 8ec260b45..b6a74fc6d 100644
--- a/MooreOnlineConf/options/online.py
+++ b/MooreOnlineConf/options/online.py
@@ -35,7 +35,7 @@ for name, configurable in allConfigurables.items():
         writer = application.setup_mbm_output(
             'Output', name=configurable.name())
         writer.MBM_maxConsumerWait = 10
-        writer.MBM_allocationSize = 400 * 1024
+        writer.MBM_allocationSize = 1024 * 1024  # bytes
 
 ApplicationMgr().ExtSvc.append(
     OnlMonitorSink(
diff --git a/MooreScripts/options/MonMBM.opts b/MooreScripts/options/MonMBM.opts
index 874b6e4c8..1b2dae0ed 100644
--- a/MooreScripts/options/MonMBM.opts
+++ b/MooreScripts/options/MonMBM.opts
@@ -4,7 +4,7 @@
 #include "$INFO_OPTIONS"
 #include "$FARMCONFIGROOT/options/Logging.opts"
 //
-OnlineEnv.MBM_setup = "-s=10000 -e=100 -u=5 -b=10 -t=1 -y -i=Events -f -c -s=1000 -e=10 -u=5 -b=10 -t=1 -y -i=Output -f -c";
+OnlineEnv.MBM_setup = "-s=10000 -e=100 -u=5 -b=10 -t=1 -y -i=Events -f -c -s=10000 -e=10 -u=5 -b=10 -t=1 -y -i=Output -f -c";
 //
 Manager.Setup               = {"Dataflow_MBMServer/MEPManager"};
 //
-- 
GitLab


From 5ee2311b01ee1514df858d565a760513e6b16b41 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Mon, 4 Mar 2024 08:16:49 +0100
Subject: [PATCH 12/24] Ignore DIM error on shutdown

---
 MooreScripts/python/MooreScripts/testbench/asyncdim.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MooreScripts/python/MooreScripts/testbench/asyncdim.py b/MooreScripts/python/MooreScripts/testbench/asyncdim.py
index 84e785dd7..bd2a1dd70 100644
--- a/MooreScripts/python/MooreScripts/testbench/asyncdim.py
+++ b/MooreScripts/python/MooreScripts/testbench/asyncdim.py
@@ -40,6 +40,7 @@ _DIM_MESSAGE_LEVELS = {
 
 
 def dim_message_handler(severity, errcode, reason):
+    if errcode == 0x30: return  # DIMDNSCNERR
     level = _DIM_MESSAGE_LEVELS[severity]
     log.log(level, f"{reason} ({errcode=})")
 
-- 
GitLab


From be15fd98359b4efacf00c97db5d58f193e28e6cd Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Mon, 4 Mar 2024 08:21:46 +0100
Subject: [PATCH 13/24] Wait for all events to be processed in default scenario

---
 .../python/MooreScripts/testbench/emulator.py | 25 ++++++++++
 .../testbench/scenarios/default.py            | 49 ++++++++++++++++---
 2 files changed, 67 insertions(+), 7 deletions(-)

diff --git a/MooreScripts/python/MooreScripts/testbench/emulator.py b/MooreScripts/python/MooreScripts/testbench/emulator.py
index 8e0b25d40..66f68f316 100644
--- a/MooreScripts/python/MooreScripts/testbench/emulator.py
+++ b/MooreScripts/python/MooreScripts/testbench/emulator.py
@@ -426,3 +426,28 @@ async def tasks_wait_for_output(tasks, type_pattern=r".*Writer.*"):
     utgids = [t.utgid for t in tasks]
     utgids = [u for u in utgids if re.match(type_pattern, u.split("_")[2])]
     return await wait_for_output(utgids)
+
+
+async def tasks_get_counter(tasks, counter_name):
+    async with AsyncExitStack() as stack:
+        services = [
+            stack.enter_context(
+                asyncdim.DimService(t.utgid + "/" + counter_name, "X"))
+            for t in tasks
+        ]
+        return [(await s.get())[1][1] for s in services]
+
+
+async def tasks_wait_for_value(tasks, counter_name, predicate):
+    async with AsyncExitStack() as stack:
+        services = [
+            stack.enter_context(
+                asyncdim.DimService(t.utgid + "/" + counter_name, "X"))
+            for t in tasks
+        ]
+        while True:
+            values = [(await s.get())[1][1] for s in services]
+            log.debug(f"values for {counter_name}: {values}")
+            # if (s := sum(values)) > value:
+            if predicate(values):
+                return values
diff --git a/MooreScripts/python/MooreScripts/testbench/scenarios/default.py b/MooreScripts/python/MooreScripts/testbench/scenarios/default.py
index d8c659518..d701906bd 100644
--- a/MooreScripts/python/MooreScripts/testbench/scenarios/default.py
+++ b/MooreScripts/python/MooreScripts/testbench/scenarios/default.py
@@ -10,6 +10,7 @@
 ###############################################################################
 import asyncio
 import logging
+import re
 from MooreScripts.testbench import emulator, asyncdim
 from MooreScripts.testbench.emulator import (
     tasks_load,
@@ -18,6 +19,8 @@ from MooreScripts.testbench.emulator import (
     tasks_wait_for_exit,
     tasks_measure_throughput,
     tasks_wait_for_output,
+    tasks_wait_for_value,
+    # async_input,
 )
 from typing import List
 
@@ -25,6 +28,15 @@ log = logging.getLogger(__name__)
 
 
 async def run(tasks: List[emulator.Task], args):
+    prod_tasks = [t for t in tasks if "Prod" in t.utgid]
+    main_tasks = [
+        t for t in tasks if re.match(r".*(HLT|Mon).*",
+                                     t.utgid.split("_")[2])
+    ]
+    if len(prod_tasks) != 1:
+        raise ValueError("There must be exactly one *Prod task")
+    prod_task = prod_tasks[0]
+
     await tasks_load(tasks)
     # TODO for some reason HLT2 publishes OFFLINE before NOT_READY, but only sometimes
     await tasks_wait_for_status(tasks, "NOT_READY", skip=["OFFLINE"])
@@ -51,14 +63,37 @@ async def run(tasks: List[emulator.Task], args):
             tasks, max_duration=args.measure_throughput)
     else:
         # wait for the reader task to get to a PAUSED state (no more input)
-        prod_task = [t for t in tasks if "Prod" in t.utgid]
-        await tasks_wait_for_status(prod_task, "PAUSED")
+        await tasks_wait_for_status(prod_tasks, "PAUSED")
 
-    # if there is a writer, wait for the output rate to be 0
-    if any("Writer" in task.utgid for task in tasks):
-        await tasks_wait_for_output(tasks)
-    await tasks_send_command(tasks, "stop")
-    await tasks_wait_for_status(tasks, "READY")
+    dim_prod_out = asyncdim.DimService(prod_task.utgid + "/Events/OUT", "X")
+
+    # stop producing new data
+    await tasks_send_command(prod_tasks, "stop")
+    await tasks_wait_for_status(prod_tasks, "READY")
+
+    # Get last published value
+    # TODO can we make it such that the number of events put in the buffer keeps being published after stop?
+    n_events_produced = next(
+        v for ts, v in reversed(await dim_prod_out.get_all()) if v is not None)
+
+    if "HLT1" in main_tasks[0].utgid:
+        # if there is a writer, wait for the output rate to be 0
+        if any("Writer" in task.utgid for task in tasks):
+            await tasks_wait_for_output(tasks)
+    else:
+        log.info(f"Waiting to process all {n_events_produced} events")
+        n_events_processed = sum(await tasks_wait_for_value(
+            main_tasks,
+            "Events/OUT",
+            lambda vs: sum(vs) >= n_events_produced,
+        ))
+        if n_events_processed > n_events_produced:
+            log.error(f"Produced {n_events_produced} but processed " +
+                    f"more: {n_events_processed}")
+
+    await tasks_send_command([t for t in tasks if t not in prod_tasks], "stop")
+    await tasks_wait_for_status([t for t in tasks if t not in prod_tasks],
+                                "READY")
 
     await tasks_send_command(tasks, "reset")
     await tasks_wait_for_status(tasks, "NOT_READY")
-- 
GitLab


From a592f141548ad3452635004000d497b3287c410c Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 12 Jul 2023 17:32:30 +0200
Subject: [PATCH 14/24] Add ODINMon task

---
 MooreScripts/job/runODINMon.sh                | 21 ++++++++
 MooreScripts/tests/options/ODINMon/Arch.xml   | 49 +++++++++++++++++++
 .../tests/options/ODINMon/OnlineEnv.opts      |  8 +++
 .../tests/options/ODINMon/OnlineEnvBase.py    | 18 +++++++
 MooreScripts/tests/qmtest/odinmon.qmt         | 34 +++++++++++++
 5 files changed, 130 insertions(+)
 create mode 100755 MooreScripts/job/runODINMon.sh
 create mode 100644 MooreScripts/tests/options/ODINMon/Arch.xml
 create mode 100644 MooreScripts/tests/options/ODINMon/OnlineEnv.opts
 create mode 100644 MooreScripts/tests/options/ODINMon/OnlineEnvBase.py
 create mode 100644 MooreScripts/tests/qmtest/odinmon.qmt

diff --git a/MooreScripts/job/runODINMon.sh b/MooreScripts/job/runODINMon.sh
new file mode 100755
index 000000000..3539d2749
--- /dev/null
+++ b/MooreScripts/job/runODINMon.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+###############################################################################
+# (c) Copyright 2000-2021 CERN for the benefit of the LHCb Collaboration      #
+#                                                                             #
+# This software is distributed under the terms of the GNU General Public      #
+# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
+#                                                                             #
+# In applying this licence, CERN does not waive the privileges and immunities #
+# granted to it by virtue of its status as an Intergovernmental Organization  #
+# or submit itself to any jurisdiction.                                       #
+###############################################################################
+set -euo pipefail
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+source "$DIR/setupTask.sh"
+
+setup_options_path MONITORING
+exec_gaudirun \
+    $MOOREONLINECONFROOT/options/tags-master.py \
+    $MOOREONLINECONFROOT/options/verbosity.py \
+    $MOOREONLINECONFROOT/options/odin.py \
+    $MOOREONLINECONFROOT/options/online.py
diff --git a/MooreScripts/tests/options/ODINMon/Arch.xml b/MooreScripts/tests/options/ODINMon/Arch.xml
new file mode 100644
index 000000000..6c8161475
--- /dev/null
+++ b/MooreScripts/tests/options/ODINMon/Arch.xml
@@ -0,0 +1,49 @@
+<!--
+    (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration
+
+    This software is distributed under the terms of the GNU General Public
+    Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".
+
+    In applying this licence, CERN does not waive the privileges and immunities
+    granted to it by virtue of its status as an Intergovernmental Organization
+    or submit itself to any jurisdiction.
+-->
+<tasks_inventory>
+
+  <task name="MBM" user="${USER}" group="${GROUP}">
+    <command>${MOORESCRIPTSROOT}/scripts/runDFTask.sh</command>
+    <argument name="-type" value="${NAME}" />
+    <argument name="-runinfo" value="${RUNINFO}" />
+    <argument name="-options" value="${MOORESCRIPTSROOT}/options/MonMBM.opts" />
+    <argument name="-class" value="Class0" />
+    <fmcparam name="utgid" value="${PARTITION}_${NODE}_${NAME}_${INSTANCE}" />
+    <fmcparam name="define" value="BINARY_TAG=${BINARY_TAG}" />
+    <fmcparam name="define" value="WORKING_DIR=${WORKING_DIR}" />
+    <timeout action="Any" value="20" />
+  </task>
+
+  <task name="MDFProd" user="${USER}" group="${GROUP}">
+    <command>${MOORESCRIPTSROOT}/scripts/runDFTask.sh</command>
+    <argument name="-type" value="${NAME}" />
+    <argument name="-runinfo" value="${RUNINFO}" />
+    <argument name="-options" value="${MOORESCRIPTSROOT}/options/MDFProd.opts" />
+    <argument name="-class" value="Class2" />
+    <fmcparam name="utgid" value="${PARTITION}_${NODE}_${NAME}_${INSTANCE}" />
+    <fmcparam name="define" value="BINARY_TAG=${BINARY_TAG}" />
+    <fmcparam name="define" value="WORKING_DIR=${WORKING_DIR}" />
+    <timeout action="Any" value="30" />
+  </task>
+
+  <task name="ODINMon" user="${USER}" group="${GROUP}" instances="NUMBER_OF_INSTANCES">
+    <command>${MOORESCRIPTSROOT}/job/runODINMon.sh</command>
+    <argument name="-type" value="${NAME}" />
+    <argument name="-runinfo" value="${RUNINFO}" />
+    <argument name="-class" value="Class1" />
+    <fmcparam name="utgid" value="${PARTITION}_${NODE}_${NAME}_${INSTANCE}" />
+    <fmcparam name="define" value="BINARY_TAG=${BINARY_TAG}" />
+    <fmcparam name="define" value="WORKING_DIR=${WORKING_DIR}" />
+    <timeout action="Any" value="120" />
+    <timeout action="load" value="20" />
+  </task>
+
+</tasks_inventory>
diff --git a/MooreScripts/tests/options/ODINMon/OnlineEnv.opts b/MooreScripts/tests/options/ODINMon/OnlineEnv.opts
new file mode 100644
index 000000000..ebd550c60
--- /dev/null
+++ b/MooreScripts/tests/options/ODINMon/OnlineEnv.opts
@@ -0,0 +1,8 @@
+OnlineEnv.PartitionID         = 65535;
+OnlineEnv.PartitionName       = "TESTBEAMGUI";
+OnlineEnv.Activity            = "PHYSICS";
+OnlineEnv.OutputLevel         = 3;
+//
+OnlineEnv.Reader_Rescan       = 1;
+OnlineEnv.Reader_Directories  = {"/scratch/rmatev"};
+OnlineEnv.Reader_FilePrefix   = "Run_0000227142_";
diff --git a/MooreScripts/tests/options/ODINMon/OnlineEnvBase.py b/MooreScripts/tests/options/ODINMon/OnlineEnvBase.py
new file mode 100644
index 000000000..04c55cd1c
--- /dev/null
+++ b/MooreScripts/tests/options/ODINMon/OnlineEnvBase.py
@@ -0,0 +1,18 @@
+###############################################################################
+# (c) Copyright 2022 CERN for the benefit of the LHCb Collaboration           #
+#                                                                             #
+# This software is distributed under the terms of the GNU General Public      #
+# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
+#                                                                             #
+# In applying this licence, CERN does not waive the privileges and immunities #
+# granted to it by virtue of its status as an Intergovernmental Organization  #
+# or submit itself to any jurisdiction.                                       #
+###############################################################################
+PartitionID = 65535
+PartitionName = "TESTBEAMGUI"
+Activity = "PHYSICS"
+HltArchitecture = "dummy"
+OnlineVersion = "v0"
+MooreVersion = "v0"
+MooreOnlineVersion = "v0"
+OutputLevel = 3
diff --git a/MooreScripts/tests/qmtest/odinmon.qmt b/MooreScripts/tests/qmtest/odinmon.qmt
new file mode 100644
index 000000000..52d4b1668
--- /dev/null
+++ b/MooreScripts/tests/qmtest/odinmon.qmt
@@ -0,0 +1,34 @@
+<?xml version="1.0" ?><!DOCTYPE extension  PUBLIC '-//QM/2.3/Extension//EN'  'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
+<!--
+    (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration
+
+    This software is distributed under the terms of the GNU General Public
+    Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".
+
+    In applying this licence, CERN does not waive the privileges and immunities
+    granted to it by virtue of its status as an Intergovernmental Organization
+    or submit itself to any jurisdiction.
+-->
+<!--
+Run the Plume monitoring task in the Online testbench
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+<argument name="program"><text>$MOORESCRIPTSROOT/scripts/testbench.py</text></argument>
+<argument name="args"><set>
+  <text>$MOORESCRIPTSROOT/tests/options/ODINMon/Arch.xml</text>
+  <text>--working-dir=odinmon</text>
+  <text>--partition=TESTODINMON</text>
+  <text>--test-file-db-key=plume-raw-data-v1.1</text>
+</set></argument>
+<argument name="use_temp_dir"><enumeral>true</enumeral></argument>
+<argument name="validator"><text>
+
+# No validator for now: only check the exit code
+
+import glob
+workdir = self._common_tmpdir
+for fn in glob.glob(workdir + "/odinmon/*.*"):
+    result[os.path.basename(fn)] = open(fn).read()
+
+</text></argument>
+</extension>
-- 
GitLab


From 14ca95928b78474593fb5138dd79a6c71581e1d9 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 12 Jul 2023 18:25:43 +0200
Subject: [PATCH 15/24] Rename algorithm to be compatible with Monet pages

---
 MooreOnlineConf/options/odin.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MooreOnlineConf/options/odin.py b/MooreOnlineConf/options/odin.py
index e2ec02297..5c9472f02 100644
--- a/MooreOnlineConf/options/odin.py
+++ b/MooreOnlineConf/options/odin.py
@@ -16,7 +16,7 @@ from PyConf.Algorithms import ODINMonitor
 
 def main():
     odin = make_odin()
-    moni = ODINMonitor(name="ODIN", Input=odin)
+    moni = ODINMonitor(name="ODINMon", Input=odin)
 
     algs = []
     if options.input_type.lower() == 'online':
-- 
GitLab


From de754e9aefe36d1b0f94bc695e426003e6a18f97 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 4 Oct 2023 12:34:20 +0200
Subject: [PATCH 16/24] Add RB monitor

---
 MooreOnlineConf/options/odin.py | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/MooreOnlineConf/options/odin.py b/MooreOnlineConf/options/odin.py
index 5c9472f02..6d5b05c0e 100644
--- a/MooreOnlineConf/options/odin.py
+++ b/MooreOnlineConf/options/odin.py
@@ -8,17 +8,21 @@
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
 #############################################################################
-from PyConf.application import make_odin
+from PyConf.application import default_raw_banks, make_odin
 from Moore import options, run_reconstruction
 from Moore.config import Reconstruction
-from PyConf.Algorithms import ODINMonitor
+from PyConf.Algorithms import ODINMonitor, HltRoutingBitsMonitor
 
 
 def main():
     odin = make_odin()
     moni = ODINMonitor(name="ODINMon", Input=odin)
 
-    algs = []
+    rb_monitor = HltRoutingBitsMonitor(
+        name="HltRoutingBitsMonitor",
+        RawBanks=default_raw_banks("HltRoutingBits"))
+
+    algs = [rb_monitor]
     if options.input_type.lower() == 'online':
         from MooreOnlineConf.utils import update_and_reset
         algs.append(update_and_reset())
-- 
GitLab


From a9b536900d7093fd7042f4df50b4c37657ecad3b Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 4 Oct 2023 12:39:56 +0200
Subject: [PATCH 17/24] Add SenderScaled in the ODINMon architecture

---
 MooreScripts/options/MDFProdToScale.opts    |  8 ++++
 MooreScripts/options/MonMBM.opts            |  2 +-
 MooreScripts/options/SenderScaled.opts      | 41 +++++++++++++++++++++
 MooreScripts/tests/options/ODINMon/Arch.xml | 14 ++++++-
 4 files changed, 63 insertions(+), 2 deletions(-)
 create mode 100644 MooreScripts/options/MDFProdToScale.opts
 create mode 100644 MooreScripts/options/SenderScaled.opts

diff --git a/MooreScripts/options/MDFProdToScale.opts b/MooreScripts/options/MDFProdToScale.opts
new file mode 100644
index 000000000..c75d3bd6d
--- /dev/null
+++ b/MooreScripts/options/MDFProdToScale.opts
@@ -0,0 +1,8 @@
+// MDF Reader options used in testing.
+// The actual file location should come from OnlineEnv.
+//
+#pragma print off
+#include "MDFProd.opts"
+//
+Reader.Buffer               = "EventsToScale";
+MEPManager.Buffers          = {"EventsToScale"};
diff --git a/MooreScripts/options/MonMBM.opts b/MooreScripts/options/MonMBM.opts
index 1b2dae0ed..0d92f2c17 100644
--- a/MooreScripts/options/MonMBM.opts
+++ b/MooreScripts/options/MonMBM.opts
@@ -4,7 +4,7 @@
 #include "$INFO_OPTIONS"
 #include "$FARMCONFIGROOT/options/Logging.opts"
 //
-OnlineEnv.MBM_setup = "-s=10000 -e=100 -u=5 -b=10 -t=1 -y -i=Events -f -c -s=10000 -e=10 -u=5 -b=10 -t=1 -y -i=Output -f -c";
+OnlineEnv.MBM_setup = "-s=10000 -e=100 -u=5 -b=10 -t=1 -y -i=EventsToScale -f -c -s=10000 -e=100 -u=5 -b=10 -t=1 -y -i=Events -f -c -s=10000 -e=10 -u=5 -b=10 -t=1 -y -i=Output -f -c";
 //
 Manager.Setup               = {"Dataflow_MBMServer/MEPManager"};
 //
diff --git a/MooreScripts/options/SenderScaled.opts b/MooreScripts/options/SenderScaled.opts
new file mode 100644
index 000000000..e8972b00d
--- /dev/null
+++ b/MooreScripts/options/SenderScaled.opts
@@ -0,0 +1,41 @@
+#pragma print off
+//------------- Default sender options: ------------------------------------------
+#include "$INFO_OPTIONS"
+#include "$FARMCONFIGROOT/options/Logging.opts"
+#include "$FARMCONFIGROOT/options/Monitoring.opts"
+Monitoring.CounterUpdateInterval = 3;
+Logger.OutputLevel   = @OnlineEnv.OutputLevel;
+//
+Manager.Services     = {"Dataflow_MBMClient/MBM",
+                        "Dataflow_MBMAccumulator/EventSelector",
+                        "Dataflow_RoutingBitsPrescaler/Prescaler",
+                        "Dataflow_UI/UI"
+};
+//
+Manager.Runable      = "EventSelector";
+//
+MBM.Buffers          = {"EventsToScale", "Events"};
+MBM.PartitionID      = @OnlineEnv.PartitionID;
+MBM.PartitionName    = @OnlineEnv.PartitionName;
+MBM.PartitionBuffers = true;
+//
+EventSelector.Input  = "EventsToScale";
+EventSelector.REQ1   = "EvType=2;TriggerMask=0xffffffff,0xffffffff,0xffffffff,0xffffffff;VetoMask=0,0,0,0;MaskType=ANY;UserType=USER;Frequency=PERC;Perc=100";
+EventSelector.PreScaler  = "Prescaler";
+EventSelector.BufferSize = 2000000;
+EventSelector.MaxEvents  = 100;
+//
+MBMWriter.Buffer      = "Events";
+//
+//
+Prescaler.StreamTypes = { "Lumi": 1, "TAE": 6, "Physics": 14, "SMOG": 15 };
+//
+// Assuming we have 140 EB servers, each selection will be limited to 50 Hz, so at most a server will send out 4 * 50 Hz, so 28 kHz in total.
+//
+Prescaler.Requirements =  {
+  "Name=TAE;EvType=2;TriggerMask=0x40,0x0,0x0,0x0;VetoMask=0,0,0,0;MaskType=ANY;UserType=USER;Frequency=PERC;Perc=100",
+  "Name=Physics;EvType=2;TriggerMask=0x4000,0x0,0x0,0x0;VetoMask=0,0,0,0;MaskType=ANY;UserType=USER;Frequency=PERC;Perc=100;RATELIMIT=50.0",
+  "Name=SMOG;EvType=2;TriggerMask=0x8000,0x0,0x0,0x0;VetoMask=0,0,0,0;MaskType=ANY;UserType=USER;Frequency=PERC;Perc=100;RATELIMIT=50.0"
+};
+//
+Manager.Algorithms   = {"Dataflow_MBMWriter/MBMWriter"};
diff --git a/MooreScripts/tests/options/ODINMon/Arch.xml b/MooreScripts/tests/options/ODINMon/Arch.xml
index 6c8161475..9e26c10cd 100644
--- a/MooreScripts/tests/options/ODINMon/Arch.xml
+++ b/MooreScripts/tests/options/ODINMon/Arch.xml
@@ -26,7 +26,7 @@
     <command>${MOORESCRIPTSROOT}/scripts/runDFTask.sh</command>
     <argument name="-type" value="${NAME}" />
     <argument name="-runinfo" value="${RUNINFO}" />
-    <argument name="-options" value="${MOORESCRIPTSROOT}/options/MDFProd.opts" />
+    <argument name="-options" value="${MOORESCRIPTSROOT}/options/MDFProdToScale.opts" />
     <argument name="-class" value="Class2" />
     <fmcparam name="utgid" value="${PARTITION}_${NODE}_${NAME}_${INSTANCE}" />
     <fmcparam name="define" value="BINARY_TAG=${BINARY_TAG}" />
@@ -34,6 +34,18 @@
     <timeout action="Any" value="30" />
   </task>
 
+  <task name="SenderScaled" user="${USER}" group="${GROUP}">
+    <command>${MOORESCRIPTSROOT}/scripts/runDFTask.sh</command>
+    <argument name="-type" value="${NAME}" />
+    <argument name="-runinfo" value="${RUNINFO}" />
+    <argument name="-options" value="${MOORESCRIPTSROOT}/options/SenderScaled.opts" />
+    <argument name="-class" value="Class1" />
+    <fmcparam name="utgid" value="${PARTITION}_${NODE}_${NAME}_${INSTANCE}" />
+    <fmcparam name="define" value="BINARY_TAG=${BINARY_TAG}" />
+    <fmcparam name="define" value="WORKING_DIR=${WORKING_DIR}" />
+    <timeout action="Any" value="30" />
+  </task>
+
   <task name="ODINMon" user="${USER}" group="${GROUP}" instances="NUMBER_OF_INSTANCES">
     <command>${MOORESCRIPTSROOT}/job/runODINMon.sh</command>
     <argument name="-type" value="${NAME}" />
-- 
GitLab


From 206a23eabad681603f1402ff0dcb0f69542f5610 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Wed, 4 Oct 2023 12:42:04 +0200
Subject: [PATCH 18/24] Add ODINMon test

---
 MooreScripts/tests/qmtest/odinmon.qmt | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/MooreScripts/tests/qmtest/odinmon.qmt b/MooreScripts/tests/qmtest/odinmon.qmt
index 52d4b1668..c9527c882 100644
--- a/MooreScripts/tests/qmtest/odinmon.qmt
+++ b/MooreScripts/tests/qmtest/odinmon.qmt
@@ -10,7 +10,7 @@
     or submit itself to any jurisdiction.
 -->
 <!--
-Run the Plume monitoring task in the Online testbench
+Run an ODINMon job in the Online testbench
 -->
 <extension class="GaudiTest.GaudiExeTest" kind="test">
 <argument name="program"><text>$MOORESCRIPTSROOT/scripts/testbench.py</text></argument>
@@ -18,7 +18,7 @@ Run the Plume monitoring task in the Online testbench
   <text>$MOORESCRIPTSROOT/tests/options/ODINMon/Arch.xml</text>
   <text>--working-dir=odinmon</text>
   <text>--partition=TESTODINMON</text>
-  <text>--test-file-db-key=plume-raw-data-v1.1</text>
+  <text>--test-file-db-key=2022_raw_hlt1_253597</text>
 </set></argument>
 <argument name="use_temp_dir"><enumeral>true</enumeral></argument>
 <argument name="validator"><text>
@@ -28,7 +28,8 @@ Run the Plume monitoring task in the Online testbench
 import glob
 workdir = self._common_tmpdir
 for fn in glob.glob(workdir + "/odinmon/*.*"):
-    result[os.path.basename(fn)] = open(fn).read()
+    if not fn.endswith(".mdf"):
+        result[os.path.basename(fn)] = open(fn).read()
 
 </text></argument>
 </extension>
-- 
GitLab


From 59d12ffeaffb0e3c50fcf445c769bd489881798d Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Fri, 2 Feb 2024 11:00:55 +0100
Subject: [PATCH 19/24] WIP: TAE stuff

---
 MooreOnlineConf/options/data_276754.py        | 30 +++++++
 MooreOnlineConf/options/odin.py               | 84 ++++++++++++++++---
 MooreScripts/tests/qmtest/odinmon_offline.qmt | 27 ++++++
 3 files changed, 128 insertions(+), 13 deletions(-)
 create mode 100644 MooreOnlineConf/options/data_276754.py
 create mode 100644 MooreScripts/tests/qmtest/odinmon_offline.qmt

diff --git a/MooreOnlineConf/options/data_276754.py b/MooreOnlineConf/options/data_276754.py
new file mode 100644
index 000000000..8d966fee5
--- /dev/null
+++ b/MooreOnlineConf/options/data_276754.py
@@ -0,0 +1,30 @@
+###############################################################################
+# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration           #
+#                                                                             #
+# This software is distributed under the terms of the GNU General Public      #
+# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
+#                                                                             #
+# In applying this licence, CERN does not waive the privileges and immunities #
+# granted to it by virtue of its status as an Intergovernmental Organization  #
+# or submit itself to any jurisdiction.                                       #
+#############################################################################
+from Moore import options
+
+# options.set_input_and_conds_from_testfiledb("2023_raw_hlt1_269939")
+options.set_input_and_conds_from_testfiledb("2023_raw_hlt1_276756_tae")
+
+options.input_files = [
+    # "/eos/lhcb/wg/rta/data/tae_2023/Run_0000269869_20230711-152221-481_R1EB18.mdf"
+    "/home/rmatev/stack/mep_tae.mdf"
+]
+options.input_type = 'MDF'
+options.geometry_version = 'trunk'
+options.conditions_version = 'master'
+options.simulation = False
+
+options.evt_max = 1000
+# options.use_iosvc = True
+# options.event_store = "EvtStoreSvc"
+
+options.histo_file = "histograms.root"
+options.monitoring_file = "monitoring.json"
diff --git a/MooreOnlineConf/options/odin.py b/MooreOnlineConf/options/odin.py
index 6d5b05c0e..e69972d0f 100644
--- a/MooreOnlineConf/options/odin.py
+++ b/MooreOnlineConf/options/odin.py
@@ -8,27 +8,85 @@
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
 #############################################################################
-from PyConf.application import default_raw_banks, make_odin
-from Moore import options, run_reconstruction
-from Moore.config import Reconstruction
-from PyConf.Algorithms import ODINMonitor, HltRoutingBitsMonitor
+from Gaudi.Configuration import ERROR
+from PyConf.application import (configure_input, configure, default_raw_banks,
+                                make_odin)
+from PyConf.control_flow import CompositeNode, NodeLogic
+from PyConf.Algorithms import ODINMonitor, HltRoutingBitsMonitor, OdinTypesFilter
+from PyConf.Algorithms import TAEODINMonitor, TESCheck
+from Moore import options
 
 
+def run_all(name, children, force_order=False):
+    return CompositeNode(
+        name,
+        children=children,
+        combine_logic=NodeLogic.NONLAZY_OR,
+        force_order=force_order)
+
+def if_then(name, predicate, then):
+    return CompositeNode(
+        name, children=[predicate, then],
+        combine_logic=NodeLogic.LAZY_AND,
+        force_order=True,
+    )
+
+def and_(name, children, force_order=False):
+    return CompositeNode(
+        name, children=children,
+        combine_logic=NodeLogic.LAZY_AND,
+        force_order=force_order,
+    )
+
+def tae_name(offset: int):
+    if not isinstance(offset, int):
+        raise TypeError("offset must be integer")
+    if abs(offset) > 9:
+        raise ValueError(f"Offsets larger than 9 not supported (got {offset})")
+    return "" if offset == 0 else f"{'Prev' if offset < 0 else 'Next'}{abs(offset)}"
+
+def tae_prefix(offset: int):
+    name = tae_name(offset)
+    return f"/Event/{name}" if name else "/Event"
+
 def main():
     odin = make_odin()
-    moni = ODINMonitor(name="ODINMon", Input=odin)
-
     rb_monitor = HltRoutingBitsMonitor(
         name="HltRoutingBitsMonitor",
         RawBanks=default_raw_banks("HltRoutingBits"))
 
-    algs = [rb_monitor]
-    if options.input_type.lower() == 'online':
-        from MooreOnlineConf.utils import update_and_reset
-        algs.append(update_and_reset())
-    algs.append(moni)
 
-    return Reconstruction('plume_moni', algs, filters=[])
+    odin_bxs = {}
+
+    decoding = []
+
+    TAE_HALF_WINDOW = 5
+    for offset in range(-TAE_HALF_WINDOW, TAE_HALF_WINDOW+1):
+        name = tae_name(offset)
+        prefix = tae_prefix(offset)
+        check = TESCheck(Inputs=[f"{prefix}/DAQ/RawEvent"], Stop=False, OutputLevel=ERROR)
+
+        with default_raw_banks.bind(tae_pos=name):
+            odin_bx = make_odin(name="Decode_ODIN" + name)
+        odin_bxs[offset] = odin_bx
+
+        decoding.append(if_then(f"TAE{name}", check, odin_bx))
+
+        # odin_monitor_prev = ODINMonitor(name="ODINMon" + name, Input=odin_bx)
+
+    taemon = TAEODINMonitor(Inputs=list(odin_bxs.values()), is_barrier=True)
+
+
+    odin_monitor = ODINMonitor(name="ODINMon", Input=odin, TAEODINLocations=[x.location for x in odin_bxs.values()], is_barrier=True)
+
+    monitors = [rb_monitor, odin, odin_monitor] +  decoding + [taemon]
+
+    return if_then(
+        "top",
+        and_("and", [OdinTypesFilter(ODIN=odin, TAEIndexMoreThan=0)]),
+        run_all("monitors", monitors),
+    )
 
 
-run_reconstruction(options, main)
+configure_input(options)
+configure(options, main())
diff --git a/MooreScripts/tests/qmtest/odinmon_offline.qmt b/MooreScripts/tests/qmtest/odinmon_offline.qmt
new file mode 100644
index 000000000..48ff0f348
--- /dev/null
+++ b/MooreScripts/tests/qmtest/odinmon_offline.qmt
@@ -0,0 +1,27 @@
+<?xml version="1.0" ?><!DOCTYPE extension  PUBLIC '-//QM/2.3/Extension//EN'  'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
+<!--
+    (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration
+
+    This software is distributed under the terms of the GNU General Public
+    Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".
+
+    In applying this licence, CERN does not waive the privileges and immunities
+    granted to it by virtue of its status as an Intergovernmental Organization
+    or submit itself to any jurisdiction.
+-->
+<!--
+Run an ODINMon job in an offline way
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+<argument name="program"><text>gaudirun.py</text></argument>
+<argument name="args"><set>
+  <text>$MOOREONLINECONFROOT/options/data_276754.py</text>
+  <text>$MOOREONLINECONFROOT/options/odin.py</text>
+</set></argument>
+<argument name="use_temp_dir"><enumeral>per-test</enumeral></argument>
+<argument name="validator"><text>
+
+# No validator for now: only check the exit code
+
+</text></argument>
+</extension>
-- 
GitLab


From 0f690901b889d9bc08bc4b4dab9699ef36a8fa33 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Thu, 7 Mar 2024 13:47:38 +0100
Subject: [PATCH 20/24] Fix detection of TAE events

---
 MooreOnlineConf/options/plume.py              |  8 +--
 .../python/MooreOnlineConf/utils.py           | 56 +++++++++++++------
 2 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/MooreOnlineConf/options/plume.py b/MooreOnlineConf/options/plume.py
index 1da288faa..c8db8986f 100644
--- a/MooreOnlineConf/options/plume.py
+++ b/MooreOnlineConf/options/plume.py
@@ -23,7 +23,6 @@ from MooreOnlineConf.utils import (
     common_monitors_node,
     passes_rb,
     RoutingBit,
-    is_tae,
     decode_tae,
     if_then,
     run_all,
@@ -44,13 +43,12 @@ def main():
         name="PlumeDigitMonitor", Input=make_plume_digits(), ODIN=make_odin())
 
     # the TAE monitor
-    tae_decoding, tae_odins, tae_data = decode_tae(
+    is_tae, tae_decoding, tae_odins, tae_data = decode_tae(
         make_plume_digits, half_window=3)
     tae_monitor = PlumeTAEMonitor(
         name="PlumeTAEMonitor",
         ODINVector=list(tae_odins.values()),
-        InputVector=list(tae_data.values()),
-        is_barrier=True)
+        InputVector=list(tae_data.values()))
 
     # assemble the control flow
     top_node = run_all(
@@ -58,7 +56,7 @@ def main():
         [
             common_monitors_node(),  # common monitoring to all tasks
             if_then("IfLUMI", passes_rb(RoutingBit.LUMI), monitor),
-            if_then("IfTAE", is_tae(),
+            if_then("IfTAE", is_tae,
                     run_all("TAE", [tae_decoding, tae_monitor])),
         ])
 
diff --git a/MooreOnlineConf/python/MooreOnlineConf/utils.py b/MooreOnlineConf/python/MooreOnlineConf/utils.py
index fd0138e08..a9bb2d6b1 100644
--- a/MooreOnlineConf/python/MooreOnlineConf/utils.py
+++ b/MooreOnlineConf/python/MooreOnlineConf/utils.py
@@ -85,39 +85,63 @@ def passes_rb(bit: RoutingBit):
         PassOnError=False)
 
 
-def is_tae():
-    odin = make_odin()
-    return OdinTypesFilter(ODIN=odin, TAEIndexMoreThan=0)
+def _has_tae_bx(offset):
+    name = tae_name(offset)
+
+    with default_raw_banks.bind(tae_pos=name):
+        tae_input = default_raw_banks(
+            "ODIN").producer.inputs["RawEventLocation"]
+        if tae_input.producer.type == Online__BanksToRawEvent:
+            tae_input = tae_input.producer.inputs["RawData"]
+
+        check = TESCheck(
+            name=f"TESCheck{name}",
+            Inputs=[tae_input.location],
+            Stop=False,
+            OutputLevel=4)
+        # data_bx = make_data(name=name)
+        return check
+
+
+# def _is_tae(half_window):
+#     # The following is not enough because an event with non-zero TAE
+#     # index might be selected for another reason.
+#     # odin = make_odin()
+#     # return OdinTypesFilter(ODIN=odin, TAEIndexMoreThan=0)
+#     children = []
+#     for offset in range(-half_window, half_window + 1):
+#         if offset != 0:
+#             children.append(_has_tae_bx(offset))
+#     return CompositeNode(
+#         "HasAnySideBX",
+#         children=children,
+#         combine_logic=NodeLogic.LAZY_OR)
 
 
 def decode_tae(make_data, half_window):
     children = []
+    check_bxs = {}
     odin_bxs = {}
     data_bxs = {}
     for offset in range(-half_window, half_window + 1):
         name = tae_name(offset)
-
         with default_raw_banks.bind(tae_pos=name):
-            tae_input = default_raw_banks(
-                "ODIN").producer.inputs["RawEventLocation"]
-            if tae_input.producer.type == Online__BanksToRawEvent:
-                tae_input = tae_input.producer.inputs["RawData"]
-
-            check = TESCheck(
-                name=f"TESCheck{name}",
-                Inputs=[tae_input.location],
-                Stop=False,
-                OutputLevel=4)
             odin_bx = make_odin(name="Decode_ODIN" + name)
             data_bx = make_data(name=name)
 
+        check_bxs[offset] = _has_tae_bx(offset)
         odin_bxs[offset] = odin_bx
         data_bxs[offset] = data_bx
         children.append(
-            and_(f"TAEBX{name}", [check, odin_bx, data_bx], force_order=True))
+            and_(
+                f"TAEBX{name}",
+                [odin_bx] + ([data_bx] if data_bx != odin_bx else []),
+                force_order=True))
 
+    # is_tae = _is_tae(half_window)
+    is_tae = and_("HasAllTAEBX", check_bxs.values())
     cf_node = run_all("DecodeTAE", children)
-    return cf_node, odin_bxs, data_bxs
+    return is_tae, cf_node, odin_bxs, data_bxs
 
 
 def update_and_reset():
-- 
GitLab


From cfd77beda5069f0c2cb8c3a668623efc0e108b28 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Thu, 7 Mar 2024 13:48:17 +0100
Subject: [PATCH 21/24] Update odinmon

---
 MooreOnlineConf/options/odin.py | 67 ++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 34 deletions(-)

diff --git a/MooreOnlineConf/options/odin.py b/MooreOnlineConf/options/odin.py
index e69972d0f..41e59769d 100644
--- a/MooreOnlineConf/options/odin.py
+++ b/MooreOnlineConf/options/odin.py
@@ -13,7 +13,13 @@ from PyConf.application import (configure_input, configure, default_raw_banks,
                                 make_odin)
 from PyConf.control_flow import CompositeNode, NodeLogic
 from PyConf.Algorithms import ODINMonitor, HltRoutingBitsMonitor, OdinTypesFilter
-from PyConf.Algorithms import TAEODINMonitor, TESCheck
+from PyConf.Algorithms import ODINTAEMonitor, TESCheck
+from MooreOnlineConf.utils import (
+    common_monitors_node,
+    decode_tae,
+    if_then,
+    run_all,
+)
 from Moore import options
 
 
@@ -49,43 +55,36 @@ def tae_prefix(offset: int):
     name = tae_name(offset)
     return f"/Event/{name}" if name else "/Event"
 
-def main():
-    odin = make_odin()
-    rb_monitor = HltRoutingBitsMonitor(
-        name="HltRoutingBitsMonitor",
-        RawBanks=default_raw_banks("HltRoutingBits"))
-
-
-    odin_bxs = {}
-
-    decoding = []
-
-    TAE_HALF_WINDOW = 5
-    for offset in range(-TAE_HALF_WINDOW, TAE_HALF_WINDOW+1):
-        name = tae_name(offset)
-        prefix = tae_prefix(offset)
-        check = TESCheck(Inputs=[f"{prefix}/DAQ/RawEvent"], Stop=False, OutputLevel=ERROR)
-
-        with default_raw_banks.bind(tae_pos=name):
-            odin_bx = make_odin(name="Decode_ODIN" + name)
-        odin_bxs[offset] = odin_bx
 
-        decoding.append(if_then(f"TAE{name}", check, odin_bx))
+def make_odin_tae(name=""):
+    return make_odin(name="Decode_ODIN" + name)
 
-        # odin_monitor_prev = ODINMonitor(name="ODINMon" + name, Input=odin_bx)
 
-    taemon = TAEODINMonitor(Inputs=list(odin_bxs.values()), is_barrier=True)
-
-
-    odin_monitor = ODINMonitor(name="ODINMon", Input=odin, TAEODINLocations=[x.location for x in odin_bxs.values()], is_barrier=True)
-
-    monitors = [rb_monitor, odin, odin_monitor] +  decoding + [taemon]
-
-    return if_then(
+def main():
+    odin = make_odin()
+    # the standard monitor
+    monitor = ODINMonitor(
+        name="ODINMon", Input=odin)
+
+    # the TAE monitor
+    is_tae, tae_decoding, tae_odins, tae_data = decode_tae(
+        make_odin_tae, half_window=3)
+    tae_monitor = ODINTAEMonitor(
+        name="ODINTAEMonitor",
+        ODINVector=list(tae_odins.values()),
+        is_barrier=True)
+
+    # assemble the control flow
+    top_node = run_all(
         "top",
-        and_("and", [OdinTypesFilter(ODIN=odin, TAEIndexMoreThan=0)]),
-        run_all("monitors", monitors),
-    )
+        [
+            common_monitors_node(),  # common monitoring to all tasks
+            monitor,
+            if_then("IfTAE", is_tae,
+                    run_all("TAE", [tae_decoding, tae_monitor])),
+        ])
+
+    return top_node
 
 
 configure_input(options)
-- 
GitLab


From 992cfb3e2fe06e4b7ce5f8f6284575443d4b2932 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Thu, 7 Mar 2024 13:54:22 +0100
Subject: [PATCH 22/24] Fixup plume config

---
 MooreOnlineConf/options/plume.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MooreOnlineConf/options/plume.py b/MooreOnlineConf/options/plume.py
index c8db8986f..81a59b133 100644
--- a/MooreOnlineConf/options/plume.py
+++ b/MooreOnlineConf/options/plume.py
@@ -33,7 +33,7 @@ from Moore import options
 def make_plume_digits(name=""):
     raw = default_raw_banks("Plume")
     digits = PlumeRawToDigits(
-        name=f"PlumeRawToDigits{name}", RawBanks=raw).Output
+        name=f"PlumeRawToDigits{name}", RawBankLocation=raw).Output
     return digits
 
 
-- 
GitLab


From 8d7eb5a421305cc01187d12ae907e130bd1a1595 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Thu, 7 Mar 2024 13:54:48 +0100
Subject: [PATCH 23/24] Fix TESCheck output level

---
 MooreOnlineConf/python/MooreOnlineConf/utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MooreOnlineConf/python/MooreOnlineConf/utils.py b/MooreOnlineConf/python/MooreOnlineConf/utils.py
index a9bb2d6b1..4a9520e14 100644
--- a/MooreOnlineConf/python/MooreOnlineConf/utils.py
+++ b/MooreOnlineConf/python/MooreOnlineConf/utils.py
@@ -98,7 +98,7 @@ def _has_tae_bx(offset):
             name=f"TESCheck{name}",
             Inputs=[tae_input.location],
             Stop=False,
-            OutputLevel=4)
+            OutputLevel=5)
         # data_bx = make_data(name=name)
         return check
 
-- 
GitLab


From f1bd352f5db7bccc25d89f8ed075aa91806411d6 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Mon, 25 Mar 2024 18:35:44 +0100
Subject: [PATCH 24/24] Take TAE half window from the online options

---
 MooreOnlineConf/options/plume.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/MooreOnlineConf/options/plume.py b/MooreOnlineConf/options/plume.py
index 81a59b133..a93e1cab3 100644
--- a/MooreOnlineConf/options/plume.py
+++ b/MooreOnlineConf/options/plume.py
@@ -29,6 +29,12 @@ from MooreOnlineConf.utils import (
 )
 from Moore import options
 
+try:
+    import OnlineEnvBase as OnlineEnv
+    TAE_HALF_WINDOW = OnlineEnv.TAE
+except ImportError:
+    TAE_HALF_WINDOW = 3
+
 
 def make_plume_digits(name=""):
     raw = default_raw_banks("Plume")
@@ -44,7 +50,7 @@ def main():
 
     # the TAE monitor
     is_tae, tae_decoding, tae_odins, tae_data = decode_tae(
-        make_plume_digits, half_window=3)
+        make_plume_digits, half_window=TAE_HALF_WINDOW)
     tae_monitor = PlumeTAEMonitor(
         name="PlumeTAEMonitor",
         ODINVector=list(tae_odins.values()),
-- 
GitLab