From 8a6b36820d998f81b6953f7a30150bc636c68dad Mon Sep 17 00:00:00 2001 From: Benjamin Labrecque Date: Tue, 14 Jul 2026 11:00:37 +0200 Subject: [PATCH] chore: start migrating dbpm3curr --- .../app/src/AGEBD-SERVICE-DBPM3CURR.py | 266 ++++++++++++++---- 1 file changed, 210 insertions(+), 56 deletions(-) diff --git a/services/dbpm3curr/current/app/src/AGEBD-SERVICE-DBPM3CURR.py b/services/dbpm3curr/current/app/src/AGEBD-SERVICE-DBPM3CURR.py index 0d9ba53..cacec1c 100644 --- a/services/dbpm3curr/current/app/src/AGEBD-SERVICE-DBPM3CURR.py +++ b/services/dbpm3curr/current/app/src/AGEBD-SERVICE-DBPM3CURR.py @@ -1,49 +1,173 @@ import time +import numpy as np import typer + from agebd.enums import LogLevel -from agebd.pv import LocalPVLink +from agebd.pv import PVLink from agebd.runner import CallbackRunner from agebd.service.base import BaseService from agebd.service.pvs import BasePVs from agebd.utils import get_pv_class, init_logging, printgetversion -from epics import dbr __version__ = printgetversion(__file__) PV = get_pv_class() +PV_PROD_READONLY = PVLink + class PVs(BasePVs): - # Option 1: - ## Service specific PVs from dedicated IOC - my_pv1 = PV("AGEBD-DBPM3CURR:AO") # TODO: is this a good PV to use? + recalibrate = PV("AGEBD-DBPM3CURR:CONTROL-RECALIBRATE") + recalibrate_apply = PV("AGEBD-DBPM3CURR:CONTROL-RECALIBRATE-APPLY") + Npts = PV("AGEBD-DBPM3CURR:CONTROL-RECALIBRATE-NPTS") - ## Service specific PVs from other IOCs - # ... + # Output PVs + charge = PV("AGEBD-DBPM3CURR:CHARGE-AVG") + calibration = PV("AGEBD-DBPM3CURR:CALIBRATION-DBPM3-2-PCT") + calibration_new = PV("AGEBD-DBPM3CURR:CALIBRATION-DBPM3-2-PCT-NEW") + + # Input PVs + dump_valve = PV_PROD_READONLY("ARS01-VVPG-0210:PLC_STATUS.VAL") + pctmast = PV_PROD_READONLY("ARS07-DPCT-0000:CURR.VAL") + # pct0100 = PV_PROD_READONLY("ARS07-DPCT-0100:CURR.VAL") + # pct0200 = PV_PROD_READONLY("ARS07-DPCT-0200:CURR.VAL") + + # BPMs + bpms = [ + PV_PROD_READONLY("ARS01-DBPM-0390:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-0760:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS01-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS02-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-0810:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS03-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-0090:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-0900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS04-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-0410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS05-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS06-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-0810:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-0840:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS07-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS08-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-0570:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS09-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS10-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-0850:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS11-DBPM-5880:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-1080:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-1460:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-1900:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-2410:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-2910:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-4220:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-4720:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-5250:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-5530:POS-STG2-I.VAL"), + PV_PROD_READONLY("ARS12-DBPM-5880:POS-STG2-I.VAL"), + ] def __init__(self, service_name: str, pv_factory): super().__init__(service_name, pv_factory) - PV = pv_factory - - # TODO: can we do this cleaner? Maybe mechanism for - # settings initial values in dev mode (e.g. ini file) - if isinstance(self.onoff, LocalPVLink): - self.onoff.value = True - - # Option 2: - ## Service specific PVs from dedicated IOC - self.my_pv2 = PV( - "AGEBD-DBPM3CURR:BO" - ) # TODO: is this a good PV to use? - - ## Service specific PVs from other IOCs - # ... - - # TODO: class attribute names usually lowercase - # usually it is advisable to trigger the mainloop of a service on changes of certain PVs: - self.CallbackPV = PV( - "AGEBD-DBPM3CURR:CALLBACK", auto_monitor=dbr.DBE_VALUE - ) # TODO: ok? class Service(BaseService[PVs]): @@ -56,41 +180,71 @@ class Service(BaseService[PVs]): ): super().__init__(name, pvs, version, sleep_interval) - # TODO: What is this for? - self.path = "sls/bd/bin" + # initialize array for DBPM3 data + self.ChargeArrayBPMs = np.empty(len(pvs.bpms)) + self.iter = 0 def update(self): - # when callback is fired, this code will be executed - if self.CallbackFired: - ####################################################################################### - ####################################################################################### - # here goes all the code that your service - # is supposed to be running when the callback is fired - time.sleep(1) - ####################################################################################### - ####################################################################################### + # set current to zero without beam + if not self.pvs.dump_valve.get() == 1: + charge = 0.0 + else: + # get the charge reading from each BPM + # ChargeArrayBPMs[:] = [pv.get() for pv in self.pvs.bpms] - # callback done - self.CallbackFired = 0 + cal = self.pvs.calibration.get() + lolo = -0.5 / cal + hihi = 500 / cal - ####################################################################################### - ####################################################################################### - # here goes all the code that your service - # is supposed to be running in any case, e.g., + for i, pv in enumerate(self.pvs.bpms): + if not pv.connected: + print("{:} is not connected".format(pv.pvname)) + val = np.nan - # update some pvs/vals - my_pv1_random_val = str(time.clock_gettime_ns(0))[-1] - self.pvs.my_pv1.put(my_pv1_random_val) - # self.pvs.my_pv2.put("MY_PV2 value") + else: + tmp = pv.get() - # get some pvs/vals - self.val1 = self.pvs.my_pv1.get() - self.val2 = self.pvs.my_pv2.get() + if lolo <= tmp <= hihi: + val = tmp + else: + val = np.nan + print("BPM charge out of range for {:}".format(pv.pvname)) - # maybe sleep to limit the update loop execution rate - time.sleep(1) - ####################################################################################### - ####################################################################################### + self.ChargeArrayBPMs[i] = val + + # calculate mean and write to PVs + charge = np.nanmean(self.ChargeArrayBPMs) + + self.pvs.charge.put(charge) + + if self.pvs.recalibrate.get() and charge > 0: + self.pvs.recalibrate.put(0) + Npts = int(self.pvs.Npts.get()) + calibration = np.zeros(Npts + 1) + self.iter = 0 + calibration[self.iter] = self.pvs.pctmast.get() / charge + self.iter += 1 + + if self.iter > 0: + if self.iter < Npts: + calibration[self.iter] = self.pvs.pctmast.get() / charge + self.iter += 1 + + elif self.iter == Npts: + calibration[self.iter] = self.pvs.pctmast.get() / charge + self.pvs.calibration_new.put(np.mean(calibration)) + self.iter = 0 + + elif self.pvs.recalibrate_apply.get(): + self.pvs.calibration.put(self.pvs.calibration_new.get()) + + def on_pause(self): + """ + Called when the service is transitioned to pause + + Set current to zero when service is paused + """ + self.pvs.charge.put(0.0) def main(