feat: migrate python service
Add new service / deploy (push) Failing after 11s

This commit is contained in:
Benjamin Labrecque
2026-07-15 16:44:58 +02:00
parent bcf69ce463
commit d1928e33d3
3 changed files with 120 additions and 41 deletions
@@ -5,6 +5,7 @@ description = "AGEBD-TAUBPM Service"
requires-python = "==3.10.*"
dependencies = [
"agebd",
"numpy>=2.0.2",
]
[dependency-groups]
@@ -1,49 +1,40 @@
import time
from time import sleep
import numpy as np
from epics import dbr
from agebd.pv import LocalPVLink
from agebd.pv import get_pv_class, get_pv_external_class
from agebd.service.base import BaseService
from agebd.service.pvs import BasePVs
from agebd.utils import printgetversion
from agebd.pv import get_pv_class
__version__ = printgetversion(__file__)
PV = get_pv_class()
PV_EXTERNAL = get_pv_external_class()
class PVs(BasePVs):
# Option 1:
## Service specific PVs from dedicated IOC
my_pv1 = PV("AGEBD-TAUBPM:AO") # TODO: is this a good PV to use?
# Service specific PVs from dedicated IOC
NsamplesTAU = PV("AGEBD-TAUBPM:CONTROL-NSAMPLES-FIT-TAU.VAL")
NsamplesROC = PV("AGEBD-TAUBPM:CONTROL-NSAMPLES-FIT-ROC.VAL")
MinCurr = PV("AGEBD-TAUBPM:CONTROL-MIN-CURR.VAL")
## Service specific PVs from other IOCs
# ...
# Service specific PVs from other IOCs
inj_active = PV_EXTERNAL("AGEBD-PARAMS:INJECTION-ACTIVE.VAL")
dump_valve = PV_EXTERNAL("ARS01-VVPG-0210:PLC_STATUS.VAL")
# Output PVs
inj_tau = PV("AGEBD-TAUBPM:TAU.VAL")
inj_tau_err = PV("AGEBD-TAUBPM:TAU-ERR.VAL")
inj_rate = PV("AGEBD-TAUBPM:INJRATE.VAL")
inj_rate_err = PV("AGEBD-TAUBPM:INJRATE-ERR.VAL")
def __init__(self, service_name: str, pv_factory=PV):
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-TAUBPM: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-TAUBPM:CALLBACK", auto_monitor=dbr.DBE_VALUE
) # TODO: ok?
self.CallbackPV = PV("AGEBD-DBPM3CURR:CURRENT-AVG.VAL", auto_monitor=dbr.DBE_VALUE)
class Service(BaseService[PVs]):
@@ -56,17 +47,102 @@ class Service(BaseService[PVs]):
):
super().__init__(name, pvs, version, sleep_interval)
# TODO: What is this for?
self.path = "sls/bd/bin"
self.reset()
def reset(self):
self.IterSinceReset = 0
self.NsamplesTAU = int(self.pvs.NsamplesTAU.get())
self.NsamplesROC = int(self.pvs.NsamplesROC.get())
NsamplesMAX = int(np.nanmax([self.NsamplesTAU, self.NsamplesROC]))
self.MinCurr = self.pvs.MinCurr.get()
self.t = np.empty(NsamplesMAX)
self.I = np.empty(NsamplesMAX)
self.inj_active = self.pvs.inj_active.get()
def update(self):
pvs = self.pvs
# 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)
conditions_cb_invalid = (
not self.inj_active == pvs.inj_active.get()
or self.CallbackValue < self.MinCurr
or not 0 <= self.CallbackValue <= 500
)
# set values to zero and wait without beam
if not pvs.dump_valve.get() == 1:
pvs.inj_tau.put(np.nan)
pvs.inj_tau_err.put(0.0)
pvs.inj_rate.put(0.0)
pvs.inj_rate_err.put(0.0)
sleep(0.5)
elif conditions_cb_invalid:
self.reset()
else:
self.t[:-1] = self.t[1:]
self.I[:-1] = self.I[1:]
self.t[-1] = self.CallbackTimestamp
self.I[-1] = self.CallbackValue
self.IterSinceReset += 1
if (
self.IterSinceReset > self.NsamplesROC
and self.IterSinceReset > self.NsamplesTAU
):
t = self.t - self.t[0]
p, pcov = np.polyfit(t, self.I, 1, cov=True)
perr = np.sqrt(np.diag(pcov))
injrate = p[0] * 60
injrate_err = perr[0] * 60
pvs.inj_rate.put(injrate)
pvs.inj_rate_err.put(injrate_err)
# injection rate in mA/hour to minimize risk of log(x) with x < 0:
injrate = injrate * 60
injrate_err = injrate_err * 60
# if injrate/p[1] < 1:
if not self.inj_active and self.IterSinceReset > self.NsamplesTAU:
tau = 1 / np.log(1 - injrate / p[1]) # (-dt in hours) / ln(1 - dI/I0)
tau_err = injrate_err * (-1) / (
(injrate - p[1]) * np.log(1 - injrate / p[1]) ** 2
) + perr[1] * (injrate_err) / (
p[1] * (injrate - p[1]) * np.log(1 - injrate / p[1]) ** 2
)
pvs.inj_tau.put(tau)
pvs.inj_tau_err.put(tau_err)
elif self.IterSinceReset > self.NsamplesROC:
t = self.t[-self.NsamplesROC :]
I = self.I[-self.NsamplesROC :]
t = t - t[0]
p, pcov = np.polyfit(t, I, 1, cov=True)
perr = np.sqrt(np.diag(pcov))
injrate = p[0] * 60
injrate_err = perr[0] * 60
pvs.inj_rate.put(injrate)
pvs.inj_rate_err.put(injrate_err)
# injection rate in mA/hour to minimize risk of log(x) with x < 0:
injrate = injrate * 60
injrate_err = injrate_err * 60
#######################################################################################
#######################################################################################
@@ -77,17 +153,15 @@ class Service(BaseService[PVs]):
#######################################################################################
# here goes all the code that your service
# is supposed to be running in any case, e.g.,
# 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")
# get some pvs/vals
self.val1 = self.pvs.my_pv1.get()
self.val2 = self.pvs.my_pv2.get()
conditions_reset = (
not self.NsamplesROC == pvs.NsamplesROC.get()
or not self.NsamplesTAU == pvs.NsamplesTAU.get()
or not self.MinCurr == pvs.MinCurr.get()
)
if conditions_reset:
self.reset()
# maybe sleep to limit the update loop execution rate
time.sleep(1)
# sleep(1)
#######################################################################################
#######################################################################################
+5 -1
View File
@@ -26,6 +26,7 @@ version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "agebd" },
{ name = "numpy" },
]
[package.dev-dependencies]
@@ -36,7 +37,10 @@ dev = [
]
[package.metadata]
requires-dist = [{ name = "agebd", editable = "../../../../packages/agebd" }]
requires-dist = [
{ name = "agebd", editable = "../../../../packages/agebd" },
{ name = "numpy", specifier = ">=2.0.2" },
]
[package.metadata.requires-dev]
dev = [