From f00f5d9ce535ef75e977aa85efe74b9581be25a1 Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Fri, 1 Mar 2024 21:50:46 +0100 Subject: [PATCH 1/9] [Snapshot] bpm bdata pushing data validity needs to be checked Todo: check bpm data implementation reimplement that bdata is not pushed forward while calculations happen --- src/dt4acc/accelerators/pyat_accelerator.py | 12 ++++++--- .../accelerators/thor_scsi_accelerator.py | 2 +- src/dt4acc/device_interface/bpm_mimikry.py | 8 +++--- src/dt4acc/view/calculation_result_view.py | 27 ++++++++++++++++++- vaccelApp/Db/beam.rec | 6 ++--- vaccelApp/Db/beam_position_monitors.rec | 19 ++++++++----- 6 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/dt4acc/accelerators/pyat_accelerator.py b/src/dt4acc/accelerators/pyat_accelerator.py index 42f6bd6..c2e47f9 100644 --- a/src/dt4acc/accelerators/pyat_accelerator.py +++ b/src/dt4acc/accelerators/pyat_accelerator.py @@ -1,3 +1,5 @@ +import logging + from ..accelerators.accelerator_impl import AcceleratorImpl from ..accelerators.proxy_factory import PyATProxyFactory from ..calculator.pyat_calculator import PyAtTwissCalculator, PyAtOrbitCalculator @@ -16,15 +18,19 @@ view = ResultView(prefix=prefix +":beam") #: todo into a controller to pass prefix as parameter at start ? elem_par_view = ElementParameterView(prefix=prefix) -bpm_names_pyat = [elem.FamName for elem in accelerator.acc if "bpm" in elem.FamName] +bpm_names_pyat = [elem.FamName for elem in accelerator.acc if "BPM" == elem.FamName[:3]] bpm_pyat = BPMMimikry(prefix=prefix, bpm_names=bpm_names_pyat) accelerator.on_new_orbit.append(view.push_orbit) accelerator.on_changed_value.append(elem_par_view.push_value) +logger = logging.getLogger("dt4acc") + + def cb(orbit_data: Orbit): - reduced_data_pyat = bpm_pyat.extract_bpm_data_from_orbit(orbit_data) - view.push_bpms(reduced_data_pyat) + # Todo: push all orbit data to beam + bpm_data = bpm_pyat.extract_bpm_data(orbit_data) + view.push_bpms(bpm_data) accelerator.on_new_orbit.append(cb) diff --git a/src/dt4acc/accelerators/thor_scsi_accelerator.py b/src/dt4acc/accelerators/thor_scsi_accelerator.py index 16318cc..cb97395 100644 --- a/src/dt4acc/accelerators/thor_scsi_accelerator.py +++ b/src/dt4acc/accelerators/thor_scsi_accelerator.py @@ -27,7 +27,7 @@ def cb(orbit_data: Orbit): - reduced_data = bpm.extract_bpm_data_from_orbit(orbit_data) + reduced_data = bpm.pyat_orbit_data_to_model(orbit_data) view.push_bpms(reduced_data) diff --git a/src/dt4acc/device_interface/bpm_mimikry.py b/src/dt4acc/device_interface/bpm_mimikry.py index 853ecc2..00fddc7 100644 --- a/src/dt4acc/device_interface/bpm_mimikry.py +++ b/src/dt4acc/device_interface/bpm_mimikry.py @@ -31,7 +31,7 @@ def __init__(self, *, prefix, bpm_names): self.prefix = prefix self.bpm_names = bpm_names - def extract_bpm_data_from_orbit(self, orbit_result: Orbit): + def extract_bpm_data(self, orbit_result: Orbit): """ Publish BPM data to EPICS. @@ -49,6 +49,6 @@ def extract_bpm_data_from_orbit(self, orbit_result: Orbit): return # find indices where the names are .. - df = pd.DataFrame(index=["x", "y"], columns=orbit_result.names, data=[orbit_result.x, orbit_result.y]).T.loc[ - self.bpm_names, :] - return Orbit(x=df.x.values, y=df.y.values, names=df.index.values, found=orbit_result.found, x0=orbit_result.x0) + df = pd.DataFrame(index=["x", "y"], columns=orbit_result.names, data=[orbit_result.x, orbit_result.y]).T + df_bpm = df.loc[self.bpm_names, :] + return Orbit(x=df_bpm.x.values, y=df_bpm.y.values, names=df_bpm.index.values, found=orbit_result.found, x0=orbit_result.x0) diff --git a/src/dt4acc/view/calculation_result_view.py b/src/dt4acc/view/calculation_result_view.py index 918d300..5e2e2ec 100644 --- a/src/dt4acc/view/calculation_result_view.py +++ b/src/dt4acc/view/calculation_result_view.py @@ -2,6 +2,7 @@ import pydev + from ..model.element_upate import ElementUpdate from ..model.orbit import Orbit from ..model.twiss import Twiss @@ -26,7 +27,7 @@ def __init__(self, *, prefix): def push_orbit(self, orbit_result: Orbit): label = f"{self.prefix}:orbit:found" logger.warning('label %s = %s type(%s)', label, orbit_result.found, type(orbit_result.found)) - pydev.iointr(label, orbit_result.found) + pydev.iointr(label, bool(orbit_result.found)) pydev.iointr(f"{self.prefix}:orbit:x", orbit_result.x) pydev.iointr(f"{self.prefix}:orbit:y", orbit_result.y) label = f"{self.prefix}:names" @@ -51,6 +52,30 @@ def push_twiss(self, twiss_result: Twiss): # fmt:on def push_bpms(self, bpm_result: Orbit): + """ + Todo: + implement that data is pushed to bdata + or make it unnecssary ... + + Warning: + Current implementation is broken + """ + import numpy as np + + n_entries = 128 + n_found = len(bpm_result.x) + bdata = np.zeros([8, n_entries], dtype=float) + bdata[0, :n_found] = bpm_result.x + bdata[1, :n_found] = bpm_result.y + label = f"{self.prefix}:bpm:bdata" + data = list(bdata.ravel()) + pydev.iointr(label, data) + logger.warning("Published bdata using label %s, n data %s, data[:10] %s", + label, len(data), data[:10]) + + return + pydev.iointr(f"{self.prefix}:bpm:x", list(bpm_result.x)) pydev.iointr(f"{self.prefix}:bpm:y", list(bpm_result.y)) pydev.iointr(f"{self.prefix}:bpm:names", list(bpm_result.names)) + diff --git a/vaccelApp/Db/beam.rec b/vaccelApp/Db/beam.rec index 9dc8c5a..2f8906a 100644 --- a/vaccelApp/Db/beam.rec +++ b/vaccelApp/Db/beam.rec @@ -25,11 +25,11 @@ record(ao, "$(PREFIX):beam:orbit:im:eps") field(TPRO, 0) } -record(bi, "$(PREFIX):beam:orbit:found") +record(longin, "$(PREFIX):beam:orbit:found") { field(DESC, "Closed orbit: fixed point found?") - field(ZNAM, "particle lost") - field(ONAM, "found") + # field(ZNAM, "particle lost") + # field(ONAM, "found") field(DTYP, "pydev") field(INP, "@pydev.iointr('$(PREFIX):beam:orbit:found')") diff --git a/vaccelApp/Db/beam_position_monitors.rec b/vaccelApp/Db/beam_position_monitors.rec index ec102b4..5ec1b63 100644 --- a/vaccelApp/Db/beam_position_monitors.rec +++ b/vaccelApp/Db/beam_position_monitors.rec @@ -34,16 +34,20 @@ record(waveform, "$(PREFIX):BPM:names") # field(TPRO, 1) } -record(waveform, "$(PREFIX):bpm:bdata") +record(waveform, "$(PREFIX):beam:bpm:bdata") { field(DESC, "BPM packed data") field(DTYP, "pydev") field(NELM, 2048) + field(NORD, 128) # So that hopefully further processing works ... # in bluesky scripts - field(INP, "@pydev.iointr('$(PREFIX)-bpm-bdata')") + # Warning: need to get label + field(INP, "@pydev.iointr('$(PREFIX):beam:bpm:bdata')") field(SCAN, "I/O Intr") field(FTVL, "DOUBLE") + field(PREC, 5) + field(TPRO, 1) field(FLNK, "$(PREFIX):bpm:bdata:count") } @@ -67,18 +71,19 @@ record(calc, "$(PREFIX):bpm:bdata:count") record(calcout, "$(PREFIX):bpm:im:bdata:trigger") { field(DESC, "BPM packed data: trigger update") - field(INPA, "$(PREFIX):dt:delayed-calcs PP") + # field(INPA, "$(PREFIX):dt:delayed-calcs PP") + # field(INPA, 0) field(VAL, 0) - field(CALC, "A") + field(CALC, "VAL + 1") field(OCAL, "1") field(DOPT, "Use OCAL") field(OUT, "$(PREFIX):bpm:im:bdata:trigger:pass PP") field(PINI, "YES") field(TPRO, 0) - field(OOPT, "When Zero") + # field(OOPT, "When Zero") + field(OOPT, "Every Time") field(SCAN, ".5 second") # field(SCAN, "2 second") - } @@ -98,7 +103,7 @@ record(bi, "$(PREFIX):bpm:im:bdata:trigger:pass") record(waveform, "$(PREFIX):MDIZ2T5G:bdata") { field(DESC, "BPM packed data: periodically updated") - field(INP, "$(PREFIX):bpm:bdata") + field(INP, "$(PREFIX):beam:bpm:bdata") field(NELM, 2048) # So that hopefully further processing works ... # in bluesky scripts From a1e9f3cf29a00dfee53bfec91411f5e14fc7fc11 Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Sat, 2 Mar 2024 12:55:17 +0100 Subject: [PATCH 2/9] [TASK] consistent record naming --- vaccelApp/Db/beam_position_monitors.rec | 18 +++++++++--------- vaccelApp/Db/dt.rec | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/vaccelApp/Db/beam_position_monitors.rec b/vaccelApp/Db/beam_position_monitors.rec index 5ec1b63..5e7dd92 100644 --- a/vaccelApp/Db/beam_position_monitors.rec +++ b/vaccelApp/Db/beam_position_monitors.rec @@ -1,37 +1,37 @@ -record(waveform, "$(PREFIX):BPM:dx") +record(waveform, "$(PREFIX):beam:bpm:dx") { field(DESC, "BPM: dx") field(EGU, "m") field(DTYP, "pydev") - field(INP, "@pydev.iointr('$(PREFIX)-bpm.dx')") + field(INP, "@pydev.iointr('$(PREFIX):beam:bpm:dx')") field(SCAN, "I/O Intr") field(NELM, 2048) field(FTVL, "DOUBLE") - # field(TPRO, 1) + field(TPRO, 1) } -record(waveform, "$(PREFIX):BPM:dy") +record(waveform, "$(PREFIX):beam:bpm:dy") { field(DESC, "BPM: dy") field(EGU, "m") field(DTYP, "pydev") - field(INP, "@pydev.iointr('$(PREFIX)-bpm.dy')") + field(INP, "@pydev.iointr('$(PREFIX):beam:bpm:dy')") field(SCAN, "I/O Intr") field(NELM, 2048) field(FTVL, "DOUBLE") - # field(TPRO, 1) + field(TPRO, 1) } -record(waveform, "$(PREFIX):BPM:names") +record(waveform, "$(PREFIX):beam:bpm:names") { field(DESC, "BPM: element names") field(DTYP, "pydev") - field(INP, "@pydev.iointr('$(PREFIX)-bpm-names')") + field(INP, "@pydev.iointr('$(PREFIX):beam:bpm:names')") field(SCAN, "I/O Intr") field(NELM, 2048) field(FTVL, "STRING") - # field(TPRO, 1) + field(TPRO, 1) } record(waveform, "$(PREFIX):beam:bpm:bdata") diff --git a/vaccelApp/Db/dt.rec b/vaccelApp/Db/dt.rec index dd8604e..dde2d1f 100644 --- a/vaccelApp/Db/dt.rec +++ b/vaccelApp/Db/dt.rec @@ -32,7 +32,7 @@ record(bi, "$(PREFIX):dt:calcs") { field(DESC, "delayed calc. actual executed") field(DTYP, "pydev") - field(INP, "@pydev.iointr('$(PREFIX)-dt-calcs')") + field(INP, "@pydev.iointr('$(PREFIX):dt:calcs')") field(SCAN, "I/O Intr") field(ZNAM, "finished") field(ONAM, "calculating") @@ -59,7 +59,7 @@ record(bi, "$(PREFIX):dt:delayed-calcs") field(DESC, "delayed calc. pending") field(DTYP, "pydev") # TODO: Review this whole record. (it is kept inactive for now) - #field(INP, "@pydev.iointr('$(PREFIX)-dt-delayed-calcs')") + #field(INP, "@pydev.iointr('$(PREFIX):dt:delayed-calcs')") field(SCAN, "I/O Intr") field(ZNAM, "none pending") field(ONAM, "pending") @@ -76,7 +76,7 @@ record(bo, "$(PREFIX):dt:do_calc_start:0") field(DTYP, "pydev") field(ZNAM, "OFF") field(ONAM, "ON") - field(OUT, "@vacc.executeCalculationsAtStartup(active=%VAL%)") + field(OUT, "@print("ERROR: triggering calculation needs to be implemented")") field(VAL, 1) field(PINI, "YES") } From 761271753dbcc2c25974e0a15c75167cf9b7c3eb Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Sat, 2 Mar 2024 12:55:47 +0100 Subject: [PATCH 3/9] [TASK] exporting bpm view --- src/dt4acc/view/calculation_result_view.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/dt4acc/view/calculation_result_view.py b/src/dt4acc/view/calculation_result_view.py index 5e2e2ec..fcb1514 100644 --- a/src/dt4acc/view/calculation_result_view.py +++ b/src/dt4acc/view/calculation_result_view.py @@ -67,15 +67,10 @@ def push_bpms(self, bpm_result: Orbit): bdata = np.zeros([8, n_entries], dtype=float) bdata[0, :n_found] = bpm_result.x bdata[1, :n_found] = bpm_result.y - label = f"{self.prefix}:bpm:bdata" - data = list(bdata.ravel()) - pydev.iointr(label, data) - logger.warning("Published bdata using label %s, n data %s, data[:10] %s", - label, len(data), data[:10]) + pydev.iointr(f"{self.prefix}:bpm:bdata", list(bdata.ravel())) - return + pydev.iointr(f"{self.prefix}:bpm:dx", list(bpm_result.x)) + pydev.iointr(f"{self.prefix}:bpm:dy", list(bpm_result.y)) + pydev.iointr(f"{self.prefix}:bpm:names", [bytes(name.encode("utf8")) for name in bpm_result.names]) - pydev.iointr(f"{self.prefix}:bpm:x", list(bpm_result.x)) - pydev.iointr(f"{self.prefix}:bpm:y", list(bpm_result.y)) - pydev.iointr(f"{self.prefix}:bpm:names", list(bpm_result.names)) From a5be0adac3202e3428220679bfcd8f9aae6f38b1 Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Mon, 4 Mar 2024 09:49:18 +0100 Subject: [PATCH 4/9] [TASK] context manager in separate module, preparing trigger for update and delayed calc --- src/dt4acc/accelerators/accelerator_impl.py | 4 +-- src/dt4acc/accelerators/element_proxies.py | 2 +- .../delay_execution.py | 25 +++++++++++++++---- src/dt4acc/{device_interface => bl}/event.py | 0 src/dt4acc/command.py | 21 +++++++++++----- 5 files changed, 38 insertions(+), 14 deletions(-) rename src/dt4acc/{device_interface => bl}/delay_execution.py (75%) rename src/dt4acc/{device_interface => bl}/event.py (100%) diff --git a/src/dt4acc/accelerators/accelerator_impl.py b/src/dt4acc/accelerators/accelerator_impl.py index 85c8297..51b9300 100644 --- a/src/dt4acc/accelerators/accelerator_impl.py +++ b/src/dt4acc/accelerators/accelerator_impl.py @@ -1,8 +1,8 @@ from collections import UserList from typing import Union -from ..device_interface.event import Event -from ..device_interface.delay_execution import DelayExecution +from dt4acc.bl.event import Event +from dt4acc.bl.delay_execution import DelayExecution from ..interfaces.accelerator_interface import AcceleratorInterface from ..interfaces.element_interface import ElementInterface diff --git a/src/dt4acc/accelerators/element_proxies.py b/src/dt4acc/accelerators/element_proxies.py index beed55d..76ae24c 100644 --- a/src/dt4acc/accelerators/element_proxies.py +++ b/src/dt4acc/accelerators/element_proxies.py @@ -1,4 +1,4 @@ -from ..device_interface.event import Event +from dt4acc.bl.event import Event from ..interfaces.element_interface import ElementInterface from ..model.element_upate import ElementUpdate diff --git a/src/dt4acc/device_interface/delay_execution.py b/src/dt4acc/bl/delay_execution.py similarity index 75% rename from src/dt4acc/device_interface/delay_execution.py rename to src/dt4acc/bl/delay_execution.py index 4e11cf3..8da89d7 100644 --- a/src/dt4acc/device_interface/delay_execution.py +++ b/src/dt4acc/bl/delay_execution.py @@ -1,15 +1,14 @@ import logging import queue import threading -import time import datetime from typing import Union +from .context_manager_with_trigger import TriggerEnterExitContextManager + from .event import Event from queue import Queue -logger = logging.getLogger("dt4acc") - class DelayExecution: """ @@ -21,14 +20,30 @@ class DelayExecution: """ def __init__(self, *, callback, delay: Union[float, None]): - self.callback = callback + self._callback = callback self.set_delay(delay) self.pending_queue = Queue() # Queue for managing pending executions self.worker_thread = threading.Thread(target=self.worker) self.worker_thread.daemon = True # Daemonize the worker thread self.worker_thread.start() - self.calculation_requested = False + self._calculation_requested = False + self.on_calculation = Event() + self.on_calculation_requested = Event() + + @property + def calculation_requested(self): + return self._calculation_requested + + @calculation_requested.setter + def calculation_requested(self, flag: bool): + flag = bool(flag) + self.on_calculation_requested.trigger(flag) + self._calculation_requested = flag + + def callback(self): + with TriggerEnterExitContextManager(self.on_calculation): + return self._callback() def set_delay(self, delay: Union[float, None]): if delay is not None: diff --git a/src/dt4acc/device_interface/event.py b/src/dt4acc/bl/event.py similarity index 100% rename from src/dt4acc/device_interface/event.py rename to src/dt4acc/bl/event.py diff --git a/src/dt4acc/command.py b/src/dt4acc/command.py index 6a5d64c..f581e0f 100644 --- a/src/dt4acc/command.py +++ b/src/dt4acc/command.py @@ -1,6 +1,8 @@ -import os - +from .bl.context_manager_with_trigger import TriggerEnterExitContextManager +from .bl.event import Event from .update_context_manager import UpdateContext +from .view.calculation_progress_view import CalculationProgressView +import os CALCULATION_ENGINE_default = os.environ["CALCULATION_ENGINE"] @@ -16,6 +18,12 @@ def publish(*, what): print(f"Need to implement publishing {what}?") +prefix = "Pierre:DT" +view = CalculationProgressView(prefix=f"{prefix}:dt:im:updates") +on_update_event = Event() +on_update_event.append(view.on_update) + + def update(*, element_id, property_name, value=None): """ What to do here: @@ -26,8 +34,9 @@ def update(*, element_id, property_name, value=None): Who takes care of the read back Is value=None a value user wants to set? - If so get an other place holder.. + If so get another placeholder.. """ - with UpdateContext(element_id=element_id, property_name=property_name, value=value, kwargs=dict()): - elem_proxy = acc.get_element(element_id) - elem_proxy.update(property_name, value) + with TriggerEnterExitContextManager(on_update_event): + with UpdateContext(element_id=element_id, property_name=property_name, value=value, kwargs=dict()): + elem_proxy = acc.get_element(element_id) + elem_proxy.update(property_name, value) From 64e945d4880af693df3ccbbcf2ebf6b5f5bd6ae9 Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Mon, 4 Mar 2024 09:50:10 +0100 Subject: [PATCH 5/9] [TASK] epics rec for flagging if update in progress --- vaccelApp/Db/dt.rec | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/vaccelApp/Db/dt.rec b/vaccelApp/Db/dt.rec index dde2d1f..6c93d60 100644 --- a/vaccelApp/Db/dt.rec +++ b/vaccelApp/Db/dt.rec @@ -28,24 +28,31 @@ record(bo, "$(PREFIX):dt:do_calc") field(TPRO, 1) } -record(bi, "$(PREFIX):dt:calcs") +record(longin, "$(PREFIX):dt:im:updates") { field(DESC, "delayed calc. actual executed") field(DTYP, "pydev") - field(INP, "@pydev.iointr('$(PREFIX):dt:calcs')") + field(INP, "@pydev.iointr('$(PREFIX):dt:im:updates')") field(SCAN, "I/O Intr") - field(ZNAM, "finished") - field(ONAM, "calculating") - field(TPRO, 0) field(VAL, 1) field(PINI, "YES") - field(FLNK, "$(PREFIX):dt:calcs:fanout") + field(TPRO, 1) + field(FLNK, "$(PREFIX):dt:updates") +} + +record(bi, "$(PREFIX):dt:updates") +{ + field(DESC, "delayed calc. actual executed") + field(INP, "$(PREFIX):dt:im:updates") + field(ZNAM, "finished") + field(ONAM, "updating") + field(FLNK, "$(PREFIX):dt:update:fanout") } # For internal reasons of the accelerator some results are # published before calcs singlas done # these can be republished after calcs goes to z -record(fanout, "$(PREFIX):dt:calcs:fanout") +record(fanout, "$(PREFIX):dt:update:fanout") { field(DESC, "Pushing calcs finished") # INformation to bpm From 3cec04dcb8fa36bb710341ec68c205a83d2a5dd4 Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Mon, 4 Mar 2024 09:54:01 +0100 Subject: [PATCH 6/9] [TASK] context manager: in separate modules --- src/dt4acc/bl/__init__.py | 0 src/dt4acc/bl/context_manager_with_trigger.py | 33 +++++++++++++++++++ src/dt4acc/view/calculation_progress_view.py | 14 ++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/dt4acc/bl/__init__.py create mode 100644 src/dt4acc/bl/context_manager_with_trigger.py create mode 100644 src/dt4acc/view/calculation_progress_view.py diff --git a/src/dt4acc/bl/__init__.py b/src/dt4acc/bl/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/dt4acc/bl/context_manager_with_trigger.py b/src/dt4acc/bl/context_manager_with_trigger.py new file mode 100644 index 0000000..c755741 --- /dev/null +++ b/src/dt4acc/bl/context_manager_with_trigger.py @@ -0,0 +1,33 @@ +import logging + +from .event import Event + +logger = logging.getLogger("dt4acc") + + +class ReportOnExitContextManager: + def __enter__(self): + logger.error("Report exit enter") + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + logger.error("Report exit") + if exc_type is None: + return + logger.error( + f"execution of trigger failed {exc_type}({exc_val})" + ) + + +class TriggerEnterExitContextManager: + def __init__(self, event: Event): + assert callable(event.trigger) + self.event = event + + def __enter__(self): + with ReportOnExitContextManager(): + self.event.trigger(True) + + def __exit__(self, exc_type, exc_val, exc_tb): + with ReportOnExitContextManager(): + self.event.trigger(False) \ No newline at end of file diff --git a/src/dt4acc/view/calculation_progress_view.py b/src/dt4acc/view/calculation_progress_view.py new file mode 100644 index 0000000..92b107e --- /dev/null +++ b/src/dt4acc/view/calculation_progress_view.py @@ -0,0 +1,14 @@ +import logging +import pydev + +logger = logging.getLogger("dt4acc") + + +class CalculationProgressView: + def __init__(self, *, prefix): + self.prefix = prefix + + def on_update(self, flag: bool): + val = int(flag) + logger.warning("sending label %s, val %s", self.prefix, val) + pydev.iointr(self.prefix, val) From 87ec5ebdb0d5f85bed59d8e2ba010f05c78ed64a Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Mon, 4 Mar 2024 11:20:03 +0100 Subject: [PATCH 7/9] [TASK] To epics world: orbit twiss calculation in progress --- src/dt4acc/accelerators/accelerator_impl.py | 7 + src/dt4acc/bl/context_manager_with_trigger.py | 9 +- src/dt4acc/bl/delay_execution.py | 6 +- src/dt4acc/bl/event.py | 4 + src/dt4acc/command.py | 17 +- src/dt4acc/view/calculation_progress_view.py | 4 +- vaccelApp/Db/beam_position_monitors.rec | 5 +- vaccelApp/Db/dt.rec | 163 +++++++++++++++--- 8 files changed, 176 insertions(+), 39 deletions(-) diff --git a/src/dt4acc/accelerators/accelerator_impl.py b/src/dt4acc/accelerators/accelerator_impl.py index 51b9300..6f88d49 100644 --- a/src/dt4acc/accelerators/accelerator_impl.py +++ b/src/dt4acc/accelerators/accelerator_impl.py @@ -37,6 +37,13 @@ def cb_orbit(): self.on_changed_value = Event() + # Shall one subscribe to the object below or should one just hand it through? + # this all seems to call for a message bus ... + self.on_orbit_calculation_request = self.orbit_calculation_delay.on_calculation_requested + self.on_orbit_calculation = self.orbit_calculation_delay.on_calculation + self.on_twiss_calculation_request = self.twiss_calculation_delay.on_calculation_requested + self.on_twiss_calculation = self.twiss_calculation_delay.on_calculation + def set_delay(self, delay: Union[float, None]): """How much to delay twiss and orbit calculation after last received comamnd """ diff --git a/src/dt4acc/bl/context_manager_with_trigger.py b/src/dt4acc/bl/context_manager_with_trigger.py index c755741..b9e08f8 100644 --- a/src/dt4acc/bl/context_manager_with_trigger.py +++ b/src/dt4acc/bl/context_manager_with_trigger.py @@ -1,17 +1,16 @@ import logging - -from .event import Event +from .event import StatusChange logger = logging.getLogger("dt4acc") class ReportOnExitContextManager: def __enter__(self): - logger.error("Report exit enter") + logger.debug("Report exit enter") pass def __exit__(self, exc_type, exc_val, exc_tb): - logger.error("Report exit") + logger.debug("Report exit") if exc_type is None: return logger.error( @@ -20,7 +19,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): class TriggerEnterExitContextManager: - def __init__(self, event: Event): + def __init__(self, event: StatusChange): assert callable(event.trigger) self.event = event diff --git a/src/dt4acc/bl/delay_execution.py b/src/dt4acc/bl/delay_execution.py index 8da89d7..061413c 100644 --- a/src/dt4acc/bl/delay_execution.py +++ b/src/dt4acc/bl/delay_execution.py @@ -6,7 +6,7 @@ from .context_manager_with_trigger import TriggerEnterExitContextManager -from .event import Event +from .event import StatusChange from queue import Queue @@ -28,8 +28,8 @@ def __init__(self, *, callback, delay: Union[float, None]): self.worker_thread.daemon = True # Daemonize the worker thread self.worker_thread.start() self._calculation_requested = False - self.on_calculation = Event() - self.on_calculation_requested = Event() + self.on_calculation = StatusChange() + self.on_calculation_requested = StatusChange() @property def calculation_requested(self): diff --git a/src/dt4acc/bl/event.py b/src/dt4acc/bl/event.py index b932b0b..86ca120 100644 --- a/src/dt4acc/bl/event.py +++ b/src/dt4acc/bl/event.py @@ -10,3 +10,7 @@ def trigger(self, obj): for callback in self: callback(obj) + +class StatusChange(Event): + def trigger(self, flag: bool): + return super().trigger(flag) \ No newline at end of file diff --git a/src/dt4acc/command.py b/src/dt4acc/command.py index f581e0f..8fff0db 100644 --- a/src/dt4acc/command.py +++ b/src/dt4acc/command.py @@ -1,7 +1,7 @@ from .bl.context_manager_with_trigger import TriggerEnterExitContextManager from .bl.event import Event from .update_context_manager import UpdateContext -from .view.calculation_progress_view import CalculationProgressView +from .view.calculation_progress_view import StatusFlagView import os CALCULATION_ENGINE_default = os.environ["CALCULATION_ENGINE"] @@ -18,11 +18,24 @@ def publish(*, what): print(f"Need to implement publishing {what}?") + +# Signals to EPICS: +# that an update is in progress prefix = "Pierre:DT" -view = CalculationProgressView(prefix=f"{prefix}:dt:im:updates") +view = StatusFlagView(prefix=f"{prefix}:dt:im:updates") on_update_event = Event() on_update_event.append(view.on_update) +# signal if orbit or twiss calculations are requested +view = StatusFlagView(prefix=f"{prefix}:dt:im:calc:orbit:exc") +acc.on_orbit_calculation.append(view.on_update) +view = StatusFlagView(prefix=f"{prefix}:dt:im:calc:orbit:req") +acc.on_orbit_calculation_request.append(view.on_update) + +view = StatusFlagView(prefix=f"{prefix}:dt:im:calc:twiss:exc") +acc.on_orbit_calculation.append(view.on_update) +view = StatusFlagView(prefix=f"{prefix}:dt:im:calc:twiss:req") +acc.on_orbit_calculation_request.append(view.on_update) def update(*, element_id, property_name, value=None): """ diff --git a/src/dt4acc/view/calculation_progress_view.py b/src/dt4acc/view/calculation_progress_view.py index 92b107e..a995a36 100644 --- a/src/dt4acc/view/calculation_progress_view.py +++ b/src/dt4acc/view/calculation_progress_view.py @@ -4,11 +4,11 @@ logger = logging.getLogger("dt4acc") -class CalculationProgressView: +class StatusFlagView: def __init__(self, *, prefix): self.prefix = prefix def on_update(self, flag: bool): val = int(flag) - logger.warning("sending label %s, val %s", self.prefix, val) pydev.iointr(self.prefix, val) + logger.debug("sent label %s, val %s", self.prefix, val) diff --git a/vaccelApp/Db/beam_position_monitors.rec b/vaccelApp/Db/beam_position_monitors.rec index 5e7dd92..45f27f2 100644 --- a/vaccelApp/Db/beam_position_monitors.rec +++ b/vaccelApp/Db/beam_position_monitors.rec @@ -71,12 +71,13 @@ record(calc, "$(PREFIX):bpm:bdata:count") record(calcout, "$(PREFIX):bpm:im:bdata:trigger") { field(DESC, "BPM packed data: trigger update") - # field(INPA, "$(PREFIX):dt:delayed-calcs PP") + # # field(INPA, 0) field(VAL, 0) field(CALC, "VAL + 1") - field(OCAL, "1") field(DOPT, "Use OCAL") + field(INPA, "$(PREFIX):dt:calc:orbit:roe PP") + field(OCAL, "A") field(OUT, "$(PREFIX):bpm:im:bdata:trigger:pass PP") field(PINI, "YES") field(TPRO, 0) diff --git a/vaccelApp/Db/dt.rec b/vaccelApp/Db/dt.rec index 6c93d60..c1256e7 100644 --- a/vaccelApp/Db/dt.rec +++ b/vaccelApp/Db/dt.rec @@ -1,5 +1,7 @@ record(bo, "$(PREFIX):dt:active") { + # Todo: send that to twin: e.g. inhibiting all updates if + # false? field(DESC, "digital twin activated?") field(VAL, 1) field(ZNAM, "OFF") @@ -7,46 +9,133 @@ record(bo, "$(PREFIX):dt:active") field(PINI, "YES") } -record(bo, "$(PREFIX):dt:do_calc") -{ - field(DESC, "calculations active?") - field(DTYP, "pydev") - # TODO: Review this whole record. (it is kept inactive for now) - # Design of command: let user inhibit "delayed calculations" - # so when 0 no "backend cacluations like orbit or twiss were executed " - # needs to reviewed how to be implemented with #update - #@update(element_id='$(ELEMENT)', property_name='dx', value=%VAL%)") - # todo: review if element id is a good name. - # why: element id is linked to lattice - # device could then be a device of the lattice or - # any other component - # perhps to name it device_name or dev_name ? - #field(OUT, "@update(element_id="dt_manager", property="do_background_calcualtions", value=%VAL%)") - # field(OUT, "@vacc.executeCalculations(active=%VAL%)") - field(ZNAM, "OFF") - field(ONAM, "ON") - field(TPRO, 1) -} record(longin, "$(PREFIX):dt:im:updates") { - field(DESC, "delayed calc. actual executed") + field(DESC, "updates in progress") field(DTYP, "pydev") field(INP, "@pydev.iointr('$(PREFIX):dt:im:updates')") field(SCAN, "I/O Intr") field(VAL, 1) field(PINI, "YES") - field(TPRO, 1) + field(TPRO, 0) field(FLNK, "$(PREFIX):dt:updates") } record(bi, "$(PREFIX):dt:updates") { - field(DESC, "delayed calc. actual executed") + field(DESC, "updates in progress?") field(INP, "$(PREFIX):dt:im:updates") field(ZNAM, "finished") field(ONAM, "updating") - field(FLNK, "$(PREFIX):dt:update:fanout") + field(FLNK, "$(PREFIX):dt:im:update:fanout") +} + +record(fanout, "$(PREFIX):dt:im:updates:fanout") +{ + field(DESC, "updates in progress?") + field(LNK0, "$(PREFIX):dt:calc:orbit:roe") +} + +record(longin, "$(PREFIX):dt:im:calc:orbit:req") +{ + field(DESC, "Orbit Calculation requested") + field(DTYP, "pydev") + # Default to 1 ... at start up ... + # it is assumed that updates for calculated parameters + # (e.g. beam position monitors) are inhibited as long as the + # requests or calculation is true + # field(VAL, 1) + # field(PINI, "YES") + field(INP, "@pydev.iointr('$(PREFIX):dt:im:calc:orbit:req')") + field(SCAN, "I/O Intr") + field(TPRO, 0) + field(FLNK, "$(PREFIX):dt:calc:orbit:req") +} + +record(longin, "$(PREFIX):dt:im:calc:orbit:exc") +{ + field(DESC, "Orbit Calculation in progress") + field(DTYP, "pydev") + # field(VAL, 1) + # field(PINI, "YES") + field(INP, "@pydev.iointr('$(PREFIX):dt:im:calc:orbit:exc')") + field(SCAN, "I/O Intr") + field(TPRO, 1) + field(FLNK, "$(PREFIX):dt:calc:orbit:exc") +} + +record(bi, "$(PREFIX):dt:calc:orbit:req") +{ + field(DESC, "Orbit calculation executing ") + field(INP, "$(PREFIX):dt:im:calc:orbit:req") + field(ZNAM, "done") + field(ONAM, "pending") + field(FLNK, "$(PREFIX):dt:calc:orbit:roe") +} + +record(bi, "$(PREFIX):dt:calc:orbit:exc") +{ + field(DESC, "Orbit calculation exectung") + field(INP, "$(PREFIX):dt:im:calc:orbit:exc") + field(ZNAM, "done") + field(ONAM, "executing") + field(FLNK, "$(PREFIX):dt:calc:orbit:roe") +} + +record(calc, "$(PREFIX):dt:calc:orbit:roe") +{ + field(DESC, "Orbit calculation requested or pending") + field(INPA, "$(PREFIX):dt:calc:orbit:req") + field(INPB, "$(PREFIX):dt:calc:orbit:exc") + # also flag progress when updates are in progress + field(INPC, "$(PREFIX):dt:updates") + field(CALC, "A | B | C") +} + + + +record(longin, "$(PREFIX):dt:im:calc:twiss:req") +{ + field(DESC, "Twiss calculation requested") + field(DTYP, "pydev") + # Default to 1 ... at start up ... + # it is assumed that updates for calculcated parameters + # (e.g. beam position monitors) are inhibited as long as the + # requests or calculation is true + field(VAL, 1) + field(PINI, "YES") + field(INP, "@pydev.iointr('$(PREFIX):dt:im:calc:twiss:req')") + field(SCAN, "I/O Intr") + field(FLNK, "$(PREFIX):dt:calc:twiss:req") +} + +record(longin, "$(PREFIX):dt:im:calc:twiss:exc") +{ + field(DESC, "Twiss calculation in progress") + field(DTYP, "pydev") + # field(VAL, 1) + # field(PINI, "YES") + field(TPRO, 1) + field(INP, "@pydev.iointr('$(PREFIX):dt:im:calc:twiss:exc')") + field(SCAN, "I/O Intr") + field(FLNK, "$(PREFIX):dt:calc:twiss:exc") +} + +record(bi, "$(PREFIX):dt:calc:twiss:req") +{ + field(DESC, "Orbit Calculation executing ") + field(INP, "$(PREFIX):dt:im:calc:twiss:req") + field(ZNAM, "done") + field(ONAM, "pending") +} + +record(bi, "$(PREFIX):dt:calc:twiss:exc") +{ + field(DESC, "Orbit Calculation exectung") + field(INP, "$(PREFIX):dt:im:calc:twiss:exc") + field(ZNAM, "done") + field(ONAM, "executing") } # For internal reasons of the accelerator some results are @@ -61,6 +150,8 @@ record(fanout, "$(PREFIX):dt:update:fanout") # field(LNK9, "$(PREFIX):bpm:im:bdata:trigger:pass") } + + record(bi, "$(PREFIX):dt:delayed-calcs") { field(DESC, "delayed calc. pending") @@ -76,6 +167,28 @@ record(bi, "$(PREFIX):dt:delayed-calcs") } + +record(bo, "$(PREFIX):dt:do_calc") +{ + field(DESC, "calculations active?") + field(DTYP, "pydev") + # TODO: Review this whole record. (it is kept inactive for now) + # Design of command: let user inhibit "delayed calculations" + # so when 0 no "backend cacluations like orbit or twiss were executed " + # needs to reviewed how to be implemented with #update + #@update(element_id='$(ELEMENT)', property_name='dx', value=%VAL%)") + # todo: review if element id is a good name. + # why: element id is linked to lattice + # device could then be a device of the lattice or + # any other component + # perhps to name it device_name or dev_name ? + #field(OUT, "@update(element_id="dt_manager", property="do_background_calcualtions", value=%VAL%)") + # field(OUT, "@vacc.executeCalculations(active=%VAL%)") + field(ZNAM, "OFF") + field(ONAM, "ON") + field(TPRO, 1) +} + record(bo, "$(PREFIX):dt:do_calc_start:0") { @@ -83,7 +196,7 @@ record(bo, "$(PREFIX):dt:do_calc_start:0") field(DTYP, "pydev") field(ZNAM, "OFF") field(ONAM, "ON") - field(OUT, "@print("ERROR: triggering calculation needs to be implemented")") + field(OUT, "@print('ERROR: triggering calculation needs to be implemented')") field(VAL, 1) field(PINI, "YES") } From 5b6858454db9d3ea259f766a7af392a9907699f4 Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Mon, 4 Mar 2024 11:26:58 +0100 Subject: [PATCH 8/9] [TASK] signal if calculation in progress => update (machine) bpm data if no calculation in progress --- vaccelApp/Db/beam.rec | 2 +- vaccelApp/Db/beam_position_monitors.rec | 12 ++++++------ vaccelApp/Db/dt.rec | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/vaccelApp/Db/beam.rec b/vaccelApp/Db/beam.rec index 2f8906a..ef0bce6 100644 --- a/vaccelApp/Db/beam.rec +++ b/vaccelApp/Db/beam.rec @@ -188,7 +188,7 @@ record(waveform, "$(PREFIX):beam:names") field(SCAN, "I/O Intr") field(NELM, 2048) field(FTVL, "STRING") - field(TPRO, 1) + field(TPRO, 0) } record(longout, "$(PREFIX):beam:publish") diff --git a/vaccelApp/Db/beam_position_monitors.rec b/vaccelApp/Db/beam_position_monitors.rec index 45f27f2..92f36d3 100644 --- a/vaccelApp/Db/beam_position_monitors.rec +++ b/vaccelApp/Db/beam_position_monitors.rec @@ -7,7 +7,7 @@ record(waveform, "$(PREFIX):beam:bpm:dx") field(SCAN, "I/O Intr") field(NELM, 2048) field(FTVL, "DOUBLE") - field(TPRO, 1) + field(TPRO, 0) } record(waveform, "$(PREFIX):beam:bpm:dy") @@ -19,7 +19,7 @@ record(waveform, "$(PREFIX):beam:bpm:dy") field(SCAN, "I/O Intr") field(NELM, 2048) field(FTVL, "DOUBLE") - field(TPRO, 1) + field(TPRO, 0) } @@ -31,7 +31,7 @@ record(waveform, "$(PREFIX):beam:bpm:names") field(SCAN, "I/O Intr") field(NELM, 2048) field(FTVL, "STRING") - field(TPRO, 1) + field(TPRO, 0) } record(waveform, "$(PREFIX):beam:bpm:bdata") @@ -47,7 +47,7 @@ record(waveform, "$(PREFIX):beam:bpm:bdata") field(SCAN, "I/O Intr") field(FTVL, "DOUBLE") field(PREC, 5) - field(TPRO, 1) + field(TPRO, 0) field(FLNK, "$(PREFIX):bpm:bdata:count") } @@ -81,8 +81,8 @@ record(calcout, "$(PREFIX):bpm:im:bdata:trigger") field(OUT, "$(PREFIX):bpm:im:bdata:trigger:pass PP") field(PINI, "YES") field(TPRO, 0) - # field(OOPT, "When Zero") - field(OOPT, "Every Time") + field(OOPT, "When Zero") + # field(OOPT, "Every Time") field(SCAN, ".5 second") # field(SCAN, "2 second") } diff --git a/vaccelApp/Db/dt.rec b/vaccelApp/Db/dt.rec index c1256e7..a82d1f3 100644 --- a/vaccelApp/Db/dt.rec +++ b/vaccelApp/Db/dt.rec @@ -61,7 +61,7 @@ record(longin, "$(PREFIX):dt:im:calc:orbit:exc") # field(PINI, "YES") field(INP, "@pydev.iointr('$(PREFIX):dt:im:calc:orbit:exc')") field(SCAN, "I/O Intr") - field(TPRO, 1) + field(TPRO, 0) field(FLNK, "$(PREFIX):dt:calc:orbit:exc") } @@ -116,7 +116,7 @@ record(longin, "$(PREFIX):dt:im:calc:twiss:exc") field(DTYP, "pydev") # field(VAL, 1) # field(PINI, "YES") - field(TPRO, 1) + field(TPRO, 0) field(INP, "@pydev.iointr('$(PREFIX):dt:im:calc:twiss:exc')") field(SCAN, "I/O Intr") field(FLNK, "$(PREFIX):dt:calc:twiss:exc") @@ -145,7 +145,7 @@ record(fanout, "$(PREFIX):dt:update:fanout") { field(DESC, "Pushing calcs finished") # INformation to bpm - field(TPRO, 1) + field(TPRO, 0) # currently deactivated # field(LNK9, "$(PREFIX):bpm:im:bdata:trigger:pass") } From ad8bff0517aeb6f4169059e4c20d1614bb4c7888 Mon Sep 17 00:00:00 2001 From: Pierre Schnizer Date: Mon, 4 Mar 2024 11:56:13 +0100 Subject: [PATCH 9/9] [TASK] inhibit (machine mimicry) bpm data update when calculation is progress --- vaccelApp/Db/beam_position_monitors.rec | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/vaccelApp/Db/beam_position_monitors.rec b/vaccelApp/Db/beam_position_monitors.rec index 92f36d3..86db3f8 100644 --- a/vaccelApp/Db/beam_position_monitors.rec +++ b/vaccelApp/Db/beam_position_monitors.rec @@ -49,7 +49,6 @@ record(waveform, "$(PREFIX):beam:bpm:bdata") field(PREC, 5) field(TPRO, 0) field(FLNK, "$(PREFIX):bpm:bdata:count") - } # A counter to use as feedback that new beam position data are available @@ -71,13 +70,12 @@ record(calc, "$(PREFIX):bpm:bdata:count") record(calcout, "$(PREFIX):bpm:im:bdata:trigger") { field(DESC, "BPM packed data: trigger update") - # - # field(INPA, 0) - field(VAL, 0) - field(CALC, "VAL + 1") - field(DOPT, "Use OCAL") + # Do not trigger on start up + field(VAL, 1) field(INPA, "$(PREFIX):dt:calc:orbit:roe PP") - field(OCAL, "A") + field(CALC, "A") + field(OCAL, "0") + field(DOPT, "Use OCAL") field(OUT, "$(PREFIX):bpm:im:bdata:trigger:pass PP") field(PINI, "YES") field(TPRO, 0) @@ -88,14 +86,16 @@ record(calcout, "$(PREFIX):bpm:im:bdata:trigger") } -# Wanted to implement it as a counter ... failed -record(bi, "$(PREFIX):bpm:im:bdata:trigger:pass") +# now always traversing from 0 to 1 ... but +# better visible on camonitor +record(calc, "$(PREFIX):bpm:im:bdata:trigger:pass") { field(DESC, "BPM packed data: trigger pass") - field(INP, "$(PREFIX):bpm:im:bdata:trigger") - field(ZNAM, "active") - field(ONAM, "inactive") - field(VAL, 1) + # field(INP, "$(PREFIX):bpm:im:bdata:trigger") + # field(ZNAM, "active") + # field(ONAM, "inactive") + field(VAL, 0) + field(CALC, "VAL + 1") field(PINI, "YES") field(FLNK, "$(PREFIX):MDIZ2T5G:bdata") @@ -108,8 +108,9 @@ record(waveform, "$(PREFIX):MDIZ2T5G:bdata") field(NELM, 2048) # So that hopefully further processing works ... # in bluesky scripts - field(PINI, "YES") + # field(PINI, "YES") field(TPRO, 0) + field(PREC, 6) field(FTVL, "DOUBLE") field(FLNK, "$(PREFIX):MDIZ2T5G:count")