feat: migrate scrubbing python service -- as much as possible :D
This commit is contained in:
@@ -5,6 +5,7 @@ description = "AGEBD-SCRUBBING Service"
|
||||
requires-python = "==3.10.*"
|
||||
dependencies = [
|
||||
"agebd",
|
||||
"numpy>=2.2.6",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import typer
|
||||
|
||||
from agebd.enums import LogLevel
|
||||
from agebd.runner import CallbackRunner
|
||||
from agebd.utils import init_logging
|
||||
from agebd_scrubbing import PVs, Service
|
||||
|
||||
from agebd_scrubbing.runner import Runner
|
||||
|
||||
# TODO: remove: cicd test 10
|
||||
|
||||
|
||||
def main(
|
||||
log_level: LogLevel = typer.Option(
|
||||
LogLevel.INFO,
|
||||
@@ -19,11 +19,15 @@ def main(
|
||||
case_sensitive=False,
|
||||
),
|
||||
):
|
||||
"""
|
||||
Uses custom/old Runner because migrating to the agebd Runner would be quite
|
||||
complex.
|
||||
"""
|
||||
init_logging(log_level)
|
||||
service_name = "SCRUBBING"
|
||||
pvs = PVs(service_name=service_name)
|
||||
service = Service(name=service_name, pvs=pvs)
|
||||
runner = CallbackRunner(service=service)
|
||||
service = Service(pvs=pvs)
|
||||
runner = Runner(svc=service)
|
||||
runner.start()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
import traceback
|
||||
from time import perf_counter, sleep
|
||||
|
||||
import numpy as np
|
||||
from epics import poll
|
||||
|
||||
from agebd_scrubbing.service import Service
|
||||
|
||||
|
||||
class Runner:
|
||||
def __init__(self, svc: Service) -> None:
|
||||
self.svc = svc
|
||||
|
||||
def start(self):
|
||||
svc = self.svc
|
||||
pvs = self.svc.pvs
|
||||
|
||||
while not pvs.abort.get():
|
||||
if pvs.onoff.get() == 1:
|
||||
# add callback for triggering the loop
|
||||
if not svc.CallbackActive:
|
||||
pvs.status.put("running")
|
||||
pvs.CallbackPV.add_callback(callback=svc.InjectionCallback, index=1, svc=svc)
|
||||
svc.CallbackActive = 1
|
||||
|
||||
if svc.ChangeTrue or perf_counter() > svc.tl + 0.1:
|
||||
# start time for next loop firing, if no vacuum changes
|
||||
svc.tl = perf_counter()
|
||||
|
||||
# reload control PVs
|
||||
svc.update()
|
||||
|
||||
# check stop/abort conitions if injection is active
|
||||
if svc.injection:
|
||||
# ensure disabled scrubbing during injection
|
||||
svc.pause_scrubbing()
|
||||
|
||||
# ensure correct injector settings
|
||||
if svc.cur > pvs.current_threshold.get():
|
||||
pvs.gun_grid.put(svc.grid_fpfb)
|
||||
else:
|
||||
pvs.gun_grid.put(svc.grid_accum)
|
||||
|
||||
# abort injection when limits exceeded
|
||||
if svc.conditions_abort_injection:
|
||||
# disable injection when upper limit reached
|
||||
svc.stop_injection()
|
||||
print("injection aborted")
|
||||
|
||||
if not svc.abort:
|
||||
svc.abort = 1
|
||||
svc.abort_t0 = perf_counter()
|
||||
|
||||
svc.set_status(svc.states["abort"], wait=60)
|
||||
|
||||
elif svc.conditions_stop_injection:
|
||||
# disable injection when upper limit reached
|
||||
svc.stop_injection()
|
||||
print("injection stopped")
|
||||
|
||||
# start scrubbing if activated
|
||||
if svc.scrubbi > 0:
|
||||
sleep(0.1)
|
||||
svc.start_scrubbing()
|
||||
|
||||
svc.set_status(svc.states["decay + scrub"])
|
||||
|
||||
else:
|
||||
svc.set_status(svc.states["decay - scrub"])
|
||||
|
||||
elif svc.info != svc.states["inject"]:
|
||||
svc.set_status(svc.states["inject"])
|
||||
|
||||
# stop injection and info if master trigger missing
|
||||
elif not svc.trig_master:
|
||||
svc.stop_injection()
|
||||
svc.set_status(svc.states["wait 4 master"])
|
||||
|
||||
# stop injection and info if master mode is not set to manual
|
||||
elif not svc.master_mode == 2:
|
||||
svc.stop_injection()
|
||||
svc.set_status(svc.states["wait 4 manual"])
|
||||
|
||||
# stop injection and wait for 60s if injection was aborted
|
||||
elif svc.abort:
|
||||
svc.stop_injection()
|
||||
dt = perf_counter() - svc.abort_t0
|
||||
|
||||
if dt > 60:
|
||||
svc.abort = 0
|
||||
|
||||
else:
|
||||
svc.set_status(svc.states["abort"], wait=60 - dt)
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
# start warmup cycling if conditions for starting injection are fulfilled
|
||||
elif svc.conditions_start_injection and svc.info not in [
|
||||
svc.states["inject"],
|
||||
svc.states["warmup"],
|
||||
]:
|
||||
svc.set_status(svc.states["warmup"])
|
||||
|
||||
# disable pingers for injection
|
||||
svc.pause_scrubbing()
|
||||
|
||||
# make sure that injection bump and pinger not both fired
|
||||
sleep(0.1)
|
||||
|
||||
# check setpoints for required amount of warmup cycles
|
||||
svc.cycles_BOIN_sp = pvs.cycles_BOIN_sp.get()
|
||||
svc.cycles_BOMB_sp = pvs.cycles_BOMB_sp.get()
|
||||
svc.cycles_BOME_sp = pvs.cycles_BOME_sp.get()
|
||||
svc.cycles_BOEX_sp = pvs.cycles_BOEX_sp.get()
|
||||
svc.cycles_SRIN_sp = pvs.cycles_SRIN_sp.get()
|
||||
|
||||
# set readback of warmup cycle counters to setpoint
|
||||
pvs.cycles_BOIN_rb.put(svc.cycles_BOIN_sp)
|
||||
pvs.cycles_BOMB_rb.put(svc.cycles_BOMB_sp)
|
||||
pvs.cycles_BOME_rb.put(svc.cycles_BOME_sp)
|
||||
pvs.cycles_BOEX_rb.put(svc.cycles_BOEX_sp)
|
||||
pvs.cycles_SRIN_rb.put(svc.cycles_SRIN_sp)
|
||||
svc.cycles_BOIN_rb = svc.cycles_BOIN_sp
|
||||
svc.cycles_BOMB_rb = svc.cycles_BOMB_sp
|
||||
svc.cycles_BOME_rb = svc.cycles_BOME_sp
|
||||
svc.cycles_BOEX_rb = svc.cycles_BOEX_sp
|
||||
svc.cycles_SRIN_rb = svc.cycles_SRIN_sp
|
||||
|
||||
# calculate highest amount of total required warmup cycles
|
||||
svc.cycles_max = np.nanmax(
|
||||
[
|
||||
svc.cycles_BOIN_sp,
|
||||
svc.cycles_BOMB_sp,
|
||||
svc.cycles_BOME_sp,
|
||||
svc.cycles_BOEX_sp,
|
||||
svc.cycles_SRIN_sp,
|
||||
]
|
||||
)
|
||||
|
||||
# calculate highest amount of remaining warmup cycles
|
||||
svc.cycles_rem = svc.cycles_max
|
||||
|
||||
# remove default callback for warmup (secretly since svc.CallbackActive = 0)
|
||||
pvs.CallbackPV.remove_callback(index=1)
|
||||
|
||||
# activate warmup callbacks
|
||||
pvs.cycles_BOIN_cnt.add_callback(
|
||||
callback=svc.WarmupCallback, index=2, svc=svc
|
||||
)
|
||||
pvs.cycles_BOMB_cnt.add_callback(
|
||||
callback=svc.WarmupCallback, index=3, svc=svc
|
||||
)
|
||||
pvs.cycles_BOME_cnt.add_callback(
|
||||
callback=svc.WarmupCallback, index=4, svc=svc
|
||||
)
|
||||
pvs.cycles_BOEX_cnt.add_callback(
|
||||
callback=svc.WarmupCallback, index=5, svc=svc
|
||||
)
|
||||
pvs.cycles_SRIN_cnt.add_callback(
|
||||
callback=svc.WarmupCallback, index=6, svc=svc
|
||||
)
|
||||
# pvs.cycles_ALL_cnt.add_callback(callback=InjectionCallback, index=7, svc=svc)
|
||||
|
||||
# set callback warmup cycle counters to 0
|
||||
svc.wait4warm = {
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-Inj-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-MA-B-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-Ramp-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-Ext-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-SR-Inj-EVRCount-I.VAL": 0,
|
||||
}
|
||||
|
||||
svc.tp = perf_counter()
|
||||
|
||||
# do top up analysis
|
||||
svc.topup_dt = (svc.tp - svc.topup_t0) / 60
|
||||
svc.topup_dI = svc.topup_I0 - svc.cur
|
||||
pvs.topup_dt.put(svc.topup_dt)
|
||||
pvs.topup_dI.put(svc.topup_dI)
|
||||
if svc.topup_I0 > 0:
|
||||
if svc.topup_dI / svc.topup_I0 < 1:
|
||||
pvs.topup_tau.put(
|
||||
(-svc.topup_dt / 60) / np.log(1 - svc.topup_dI / svc.topup_I0)
|
||||
)
|
||||
|
||||
elif svc.info == svc.states["warmup"]:
|
||||
if svc.cycles_rem < 1:
|
||||
# start injection
|
||||
svc.start_injection()
|
||||
|
||||
# reactivate default callback by notifying mainloop of its deactivation
|
||||
svc.CallbackActive = 0
|
||||
|
||||
# disable warmup callbacks
|
||||
pvs.cycles_BOIN_cnt.remove_callback(index=2)
|
||||
pvs.cycles_BOMB_cnt.remove_callback(index=3)
|
||||
pvs.cycles_BOME_cnt.remove_callback(index=4)
|
||||
pvs.cycles_BOEX_cnt.remove_callback(index=5)
|
||||
pvs.cycles_SRIN_cnt.remove_callback(index=6)
|
||||
pvs.cycles_ALL_cnt.remove_callback(index=7)
|
||||
|
||||
svc.set_status(svc.states["inject"])
|
||||
|
||||
else:
|
||||
svc.prepare_injection()
|
||||
|
||||
elif svc.info in [svc.states["pause"], svc.states["wait 4 master"]]:
|
||||
if svc.injection:
|
||||
svc.set_status(svc.states["inject"])
|
||||
else:
|
||||
if pvs.trig_pinger_h.get() and pvs.trig_pinger_v.get():
|
||||
svc.set_status(svc.states["decay + scrub"])
|
||||
else:
|
||||
svc.set_status(svc.states["decay - scrub"])
|
||||
|
||||
# stop scrubbing when deactivated
|
||||
elif svc.scrubbi == 0 and svc.scrubbilast > 0:
|
||||
svc.stop_scrubbing()
|
||||
svc.set_status(svc.states["decay - scrub"])
|
||||
|
||||
# if scrubbing activated and lifetime drops below 1 hour --> stop scrubbing
|
||||
elif svc.scrubbi > 0 and svc.tau < 1:
|
||||
svc.pause_scrubbing()
|
||||
svc.set_status(svc.states["decay - scrub"])
|
||||
print("Scrubbing aborted, tau < 1")
|
||||
|
||||
svc.ChangeTrue = 0
|
||||
|
||||
# inform watchdog(s)
|
||||
pvs.running.put(0)
|
||||
|
||||
# wait for callback to fire
|
||||
poll(evt=1.0e-5, iot=0.1)
|
||||
|
||||
else:
|
||||
# WHILE PAUSED REBOOT OF THE SCRUBBING IOC SHOULD NOT DISTURB THE SERVICE
|
||||
|
||||
if svc.CallbackActive:
|
||||
# remove callback
|
||||
pvs.CallbackPV.remove_callback(index=1)
|
||||
svc.CallbackActive = 0
|
||||
|
||||
# reload control PVs
|
||||
svc.update()
|
||||
|
||||
# disable injection and pingers when first paused
|
||||
if svc.info != svc.states["pause"]:
|
||||
svc.stop_injection()
|
||||
svc.pause_scrubbing()
|
||||
|
||||
svc.set_status(svc.states["pause"])
|
||||
pvs.status.put("paused")
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
# sleep and inform watchdog(s)
|
||||
pvs.alive.put(0)
|
||||
|
||||
if pvs.abort.get():
|
||||
# confirm clean abort
|
||||
pvs.status.put("killed")
|
||||
print("Got Killed by Master Service!")
|
||||
pvs.abortreq.put(0)
|
||||
|
||||
# elif svc.crashed:
|
||||
|
||||
# # confirm crash
|
||||
# pvs.status.put('crashed')
|
||||
# print('Service crashed on exception!')
|
||||
# print('----- svc.exception.__traceback__ -----')
|
||||
# print(svc.exception.__traceback__)
|
||||
# print()
|
||||
# print('----- traceback.format_exc() -----')
|
||||
# print(traceback.format_exc())
|
||||
|
||||
else:
|
||||
# confirm unknown exit
|
||||
pvs.status.put("unknown crash")
|
||||
print("Service crashed without exception!")
|
||||
print(traceback.format_exc())
|
||||
@@ -1,12 +1,12 @@
|
||||
import time
|
||||
from datetime import datetime
|
||||
from time import perf_counter, sleep
|
||||
|
||||
from epics import dbr
|
||||
import numpy as np
|
||||
from epics import PV
|
||||
|
||||
from agebd.pv import LocalPVLink
|
||||
from agebd.service.base import BaseService
|
||||
from agebd.pv import get_pv_class
|
||||
from agebd.service.pvs import BasePVs
|
||||
from agebd.utils import printgetversion
|
||||
from agebd.pv import get_pv_class
|
||||
|
||||
__version__ = printgetversion(__file__)
|
||||
|
||||
@@ -14,80 +14,494 @@ PV = get_pv_class()
|
||||
|
||||
|
||||
class PVs(BasePVs):
|
||||
# Option 1:
|
||||
## Service specific PVs from dedicated IOC
|
||||
my_pv1 = PV("AGEBD-SCRUBBING:AO") # TODO: is this a good PV to use?
|
||||
cycles_BOIN_sp = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOIN-SP.VAL")
|
||||
cycles_BOIN_rb = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOIN-RB.VAL")
|
||||
cycles_BOIN_cnt = PV("AGETI-CVME-MASTER-TMA:Evt-BO-Inj-EVRCount-I.VAL", auto_monitor=True)
|
||||
cycles_BOMB_sp = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOMB-SP.VAL")
|
||||
cycles_BOMB_rb = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOMB-RB.VAL")
|
||||
cycles_BOMB_cnt = PV("AGETI-CVME-MASTER-TMA:Evt-BO-MA-B-EVRCount-I.VAL", auto_monitor=True)
|
||||
cycles_BOME_sp = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOME-SP.VAL")
|
||||
cycles_BOME_rb = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOME-RB.VAL")
|
||||
cycles_BOME_cnt = PV("AGETI-CVME-MASTER-TMA:Evt-BO-Ramp-EVRCount-I.VAL", auto_monitor=True)
|
||||
cycles_BOEX_sp = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOEX-SP.VAL")
|
||||
cycles_BOEX_rb = PV("AGEBD-SCRUBBING:WARMUPCYCLES-BOEX-RB.VAL")
|
||||
cycles_BOEX_cnt = PV("AGETI-CVME-MASTER-TMA:Evt-BO-Ext-EVRCount-I.VAL", auto_monitor=True)
|
||||
cycles_SRIN_sp = PV("AGEBD-SCRUBBING:WARMUPCYCLES-SRIN-SP.VAL")
|
||||
cycles_SRIN_rb = PV("AGEBD-SCRUBBING:WARMUPCYCLES-SRIN-RB.VAL")
|
||||
cycles_SRIN_cnt = PV("AGETI-CVME-MASTER-TMA:Evt-SR-Inj-EVRCount-I.VAL", auto_monitor=True)
|
||||
cycles_ALL_cnt = PV("AGETI-CVME-MASTER-TMA:Event-Seq-Cnt-I.VAL", auto_monitor=True)
|
||||
|
||||
## Service specific PVs from other IOCs
|
||||
# ...
|
||||
## Control PVs
|
||||
# top-up control mode
|
||||
# topumod = PV('.VAL')
|
||||
# top-up mode vacuum limits
|
||||
vac_max = PV("AGEBD-SCRUBBING:CONTROL-VACUUM-MAX.VAL")
|
||||
vac_min = PV("AGEBD-SCRUBBING:CONTROL-VACUUM-MIN.VAL")
|
||||
# top-up mode current limits
|
||||
cur_max = PV("AGEBD-SCRUBBING:CONTROL-CURRENT-MAX.VAL")
|
||||
cur_min = PV("AGEBD-SCRUBBING:CONTROL-CURRENT-MIN.VAL")
|
||||
# top-up mode time limits for injection and decay (periodic refill)
|
||||
tim_inj = PV("AGEBD-SCRUBBING:CONTROL-TIME-MAX.VAL")
|
||||
tim_dec = PV("AGEBD-SCRUBBING:CONTROL-TIME-MIN.VAL")
|
||||
# mbfb gain setpoints for scrubbing
|
||||
mbfbx_gain_sp = PV("AGEBD-SCRUBBING:MBFBX-GAIN-SP.VAL")
|
||||
mbfby_gain_sp = PV("AGEBD-SCRUBBING:MBFBY-GAIN-SP.VAL")
|
||||
mbfbx_gain = PV("ARSGE-CECL-MBFB:MBFB-T-X-DDS-GAIN.VAL")
|
||||
mbfby_gain = PV("ARSGE-CECL-MBFB:MBFB-T-Y-DDS-GAIN.VAL")
|
||||
|
||||
# RDP Scan
|
||||
# pause : set gain to 0 as normal, get current sweep freq, stop sweep, set initial freq of sweep to current freq
|
||||
# start : set gain to sp (i.e., 100) as normal, start sweep
|
||||
mbfby_freq_sp = PV("ARSGE-CECL-MBFB:MBFB-T-Y-DDS-FRQ.VAL")
|
||||
mbfby_sweep_onoff = PV("ARSGE-CECL-MBFB:MBFB-T-Y-DDS-SWEEP-EN.VAL")
|
||||
mbfby_sweepfreq_start = PV("ARSGE-CECL-MBFB:MBFB-T-Y-DDS-SWEEP-FREQ-START.VAL")
|
||||
mbfby_sweepfreq_rb = PV("ARSGE-CECL-MBFB:MBFB-T-Y-DDS-SWEEP-FRQ-RB-REG.VAL")
|
||||
|
||||
vac_dyn = PV("AGEBD-SCRUBBING:CONTROL-VACUUM-DYN.VAL")
|
||||
vac_dyn_frac = PV("AGEBD-SCRUBBING:CONTROL-VACUUM-DYN-FRAC.VAL")
|
||||
|
||||
# grd_max = PV('AGEBD-SCRUBBING:CONTROL-GUNGRID-MAX.VAL')
|
||||
grid_accum = PV("AGEBD-SCRUBBING:CONTROL-GUNGRID-ACCUM.VAL")
|
||||
grid_fpfb = PV("AGEBD-SCRUBBING:CONTROL-GUNGRID-FPFB.VAL")
|
||||
# current above which the feedback mode is started
|
||||
current_threshold = PV("AGEBD-TIMING:CURRENT-THRESHOLD.VAL")
|
||||
|
||||
scrubbing = PV("AGEBD-SCRUBBING:CONTROL-SCRUB.VAL")
|
||||
|
||||
# Input PVs
|
||||
vacs = [
|
||||
# PV('ARIVA-VMAVE:PRESS-MAX.VAL'), # VCS gauges in the Arcs + Straights
|
||||
# PV('ARIVA-VMAVE-FE:PRESS-MAX.VAL'), # VCS gauges in the ring to always the first one after the FE-Valve
|
||||
# PV('ARIVA-VMAVE-ALL:PRESS-MAX.VAL'), # Max of the two above
|
||||
# PV('ARIVA-VMAVE-PSH:PRESS-MAX.VAL'), # VCS gauges in the Photon Shutters/Beam Stoppers of the existing beamlines
|
||||
PV("ARIVA-VMAVE-ALL:PRESS-MAX.VAL"), # Max of three signals: Ring/FE/PSH
|
||||
PV("ARS07-VMCC-0120:PRESSURE.VAL"),
|
||||
]
|
||||
vac_all = PV("ARIVA-VMAVE-ALL:PRESS-MAX.VAL")
|
||||
vac_acc = [
|
||||
PV("ARIVA-VMAVE:PRESS-MAX.VAL"),
|
||||
PV("ARS07-VMCC-0120:PRESSURE.VAL"),
|
||||
]
|
||||
# vac_fe = PV('ARIVA-VMAVE-FE:PRESS-MAX.VAL')
|
||||
# cur = PV('AGEBD-DBPM3CURR:CURRENT-AVG.VAL')
|
||||
cur = PV("ARS07-DPCT-0000:CURR.VAL")
|
||||
# cur1 = PV('ARS07-CECL-DPCT:BDC-DPCT100-SCALED')
|
||||
# cur2 = PV('ARS07-CECL-DPCT2:BDC-DPCT200-SCALED')
|
||||
# curuse = PV('ARS07-DPCT-0000:CHOICE-RB')
|
||||
|
||||
injguard = PV("AGEBD-INJECTIONGUARD:CHECK-SUM.VAL")
|
||||
|
||||
injactive = PV("AGEBD-PARAMS:INJECTION-ACTIVE")
|
||||
|
||||
tau = PV("AGEBD-PARAMS:LIFETIME.VAL")
|
||||
|
||||
# Regulated PVs
|
||||
trig_firego = PV("AGEBD-TIMING:GO.VAL")
|
||||
trig_master = PV("AGETI-CVME-MASTER-TMA:SR-Inj-Status-Sel.VAL")
|
||||
master_mode = PV("AGETI-CVME-MASTER-TMA:FP-Mode-Sel.VAL")
|
||||
trig_secret = PV("AGEBD-INJECTIONGUARD:STATUS-SECRET-MASTER.VAL")
|
||||
# trig_guntxb = PV('ALIRF-VME-A-GUN:CH1-MODE.VAL')
|
||||
# AGETI-CVME-MASTER-TMA:Evt-LI-Gun-Ena-Sel
|
||||
# trig_gunevt = PV('AGETI-CVME-MASTER-TMA:Evt-LI-Gun-Ena-Sel.VAL')
|
||||
# AGETI-CVME-MASTER-TMA:Evt-BO-Inj-Ena-Sel
|
||||
trig_booinj = PV("AGETI-CVME-MASTER-TMA:Evt-BO-Inj-Ena-Sel.VAL")
|
||||
# AGETI-CVME-MASTER-TMA:Evt-BO-MA-B-Ena-Sel
|
||||
# trig_boormp = PV('AGETI-CVME-MASTER-TMA:Evt-Old-BO-HW-Ena-Sel.VAL')
|
||||
trig_bormp1 = PV("AGETI-CVME-MASTER-TMA:Evt-BO-MA-B-Ena-Sel.VAL")
|
||||
# AGETI-CVME-MASTER-TMA:Evt-BO-Ramp-Ena-Sel
|
||||
trig_bormp2 = PV("AGETI-CVME-MASTER-TMA:Evt-BO-Ramp-Ena-Sel.VAL")
|
||||
trig_booext = PV(
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-Ext-Ena-Sel.VAL"
|
||||
) # Alias AGETI-CVME-MASTER-TMA:Evt-10
|
||||
trig_rininj = PV(
|
||||
"AGETI-CVME-MASTER-TMA:Evt-SR-Inj-Ena-Sel.VAL"
|
||||
) # Alias AGETI-CVME-MASTER-TMA:Evt-26
|
||||
trig_pinger_h = PV(
|
||||
"AGETI-CVME-MASTER-TMA:Evt-SR-Pinger-H-Ena-Sel.VAL"
|
||||
) # Alias AGETI-CVME-MASTER-TMA:Evt-14-Ena-Sel
|
||||
trig_pinger_v = PV(
|
||||
"AGETI-CVME-MASTER-TMA:Evt-SR-Pinger-V-Ena-Sel.VAL"
|
||||
) # Alias AGETI-CVME-MASTER-TMA:Evt-14-Ena-Sel
|
||||
|
||||
# # injection follow bucket workaround for linac (https://elog-gfa.psi.ch/SLS/64299)
|
||||
# # Before making injection, disable the LLRF phase feedback ...
|
||||
# linac_llrf_phaseFB1 = PV('ALIRF-A1-LLE-DSP:PHASE-FB-SWITCH.VAL')
|
||||
# linac_llrf_phaseFB2 = PV('ALIRF-A2-LLE-DSP:PHASE-FB-SWITCH.VAL')
|
||||
# # ... and disable the phase alarm by setting the phase limits
|
||||
# linac_llrf_phaseFB_PBU_HIHI = PV('ALIRF-PBU:Ph-COMP.HIHI')
|
||||
# linac_llrf_phaseFB_FBU_HIHI = PV('ALIRF-FBU:Ph-COMP.HIHI')
|
||||
# linac_llrf_phaseFB_MOD1_HIHI = PV('ALIRF-AS-1:Ph-COMP.HIHI')
|
||||
# linac_llrf_phaseFB_MOD2_HIHI = PV('ALIRF-AS-2:Ph-COMP.HIHI')
|
||||
# linac_llrf_phaseFB_PBU_LOLO = PV('ALIRF-PBU:Ph-COMP.LOLO')
|
||||
# linac_llrf_phaseFB_FBU_LOLO = PV('ALIRF-FBU:Ph-COMP.LOLO')
|
||||
# linac_llrf_phaseFB_MOD1_LOLO = PV('ALIRF-AS-1:Ph-COMP.LOLO')
|
||||
# linac_llrf_phaseFB_MOD2_LOLO = PV('ALIRF-AS-2:Ph-COMP.LOLO')
|
||||
|
||||
gun_grid = PV("ALIRF-GUN-GRID:V-SET.VAL")
|
||||
# pinger_v = PV('ARS01-MKIK-0320:CTRL-ON-OFF.VAL')
|
||||
# pinger_h = PV('ARS01-MKIK-0290:CTRL-ON-OFF.VAL')
|
||||
|
||||
topup_dt = PV("AGEBD-SCRUBBING:TOPUP-DELTAT.VAL")
|
||||
topup_dI = PV("AGEBD-SCRUBBING:TOPUP-DELTAI.VAL")
|
||||
topup_tau = PV("AGEBD-SCRUBBING:TOPUP-TAU.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-SCRUBBING: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-SCRUBBING:CALLBACK", auto_monitor=dbr.DBE_VALUE
|
||||
) # TODO: ok?
|
||||
# Service Callback PVs
|
||||
self.CallbackPV = PV("AGETI-CVME-MASTER-TMA:Evt-Old-BO-SYN.VALA", auto_monitor=True)
|
||||
|
||||
|
||||
class Service(BaseService[PVs]):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
pvs: PVs,
|
||||
version: str = __version__,
|
||||
sleep_interval: float = 0.1,
|
||||
):
|
||||
super().__init__(name, pvs, version, sleep_interval)
|
||||
class Service:
|
||||
def __init__(self, pvs: PVs):
|
||||
self.pvs = pvs
|
||||
|
||||
# TODO: What is this for?
|
||||
self.path = "sls/bd/bin"
|
||||
self.pvs.started.put(str(datetime.now())[:-7])
|
||||
self.pvs.version.put(__version__)
|
||||
self.pvs.info.put("initialized")
|
||||
|
||||
self.cycles_BOIN_sp = pvs.cycles_BOIN_sp.get()
|
||||
self.cycles_BOIN_rb = pvs.cycles_BOIN_rb.get()
|
||||
self.cycles_BOIN_cnt = pvs.cycles_BOIN_cnt.get()
|
||||
self.cycles_BOMB_sp = pvs.cycles_BOMB_sp.get()
|
||||
self.cycles_BOMB_rb = pvs.cycles_BOMB_rb.get()
|
||||
self.cycles_BOMB_cnt = pvs.cycles_BOMB_cnt.get()
|
||||
self.cycles_BOME_sp = pvs.cycles_BOME_sp.get()
|
||||
self.cycles_BOME_rb = pvs.cycles_BOME_rb.get()
|
||||
self.cycles_BOME_cnt = pvs.cycles_BOME_cnt.get()
|
||||
self.cycles_BOEX_sp = pvs.cycles_BOEX_sp.get()
|
||||
self.cycles_BOEX_rb = pvs.cycles_BOEX_rb.get()
|
||||
self.cycles_BOEX_cnt = pvs.cycles_BOEX_cnt.get()
|
||||
self.cycles_SRIN_sp = pvs.cycles_SRIN_sp.get()
|
||||
self.cycles_SRIN_rb = pvs.cycles_SRIN_rb.get()
|
||||
self.cycles_SRIN_cnt = pvs.cycles_SRIN_cnt.get()
|
||||
self.timerthreadready = 1
|
||||
|
||||
self.cycles_max = np.nanmax(
|
||||
[
|
||||
self.cycles_BOIN_sp,
|
||||
self.cycles_BOMB_sp,
|
||||
self.cycles_BOME_sp,
|
||||
self.cycles_BOEX_sp,
|
||||
self.cycles_SRIN_sp,
|
||||
]
|
||||
)
|
||||
|
||||
self.cycles_rem = self.cycles_max
|
||||
|
||||
self.wait4warm = {
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-Inj-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-MA-B-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-Ramp-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-BO-Ext-EVRCount-I.VAL": 0,
|
||||
"AGETI-CVME-MASTER-TMA:Evt-SR-Inj-EVRCount-I.VAL": 0,
|
||||
}
|
||||
|
||||
self.vac = np.max([pv.get() for pv in pvs.vacs])
|
||||
# self.vac_acc = pvs.vac_acc.get()
|
||||
self.vac_acc = np.max([pv.get() for pv in pvs.vac_acc])
|
||||
# self.vac = pvs.vac_fe.get()
|
||||
self.vac_array = np.repeat(self.vac_acc, 10)
|
||||
self.vac_dyn = self.vac * pvs.vac_dyn_frac.get()
|
||||
pvs.vac_dyn.put(self.vac_dyn * 1e9)
|
||||
self.cur = 420
|
||||
self.scrubbi = pvs.scrubbing.get()
|
||||
self.scrubbilast = self.scrubbi
|
||||
|
||||
self.mbfbx_sp = int(pvs.mbfbx_gain_sp.get())
|
||||
self.mbfby_sp = int(pvs.mbfby_gain_sp.get())
|
||||
self.mbfby_sweepfreq_rb = np.round(pvs.mbfby_sweepfreq_rb.get())
|
||||
|
||||
self.topup_t0 = perf_counter()
|
||||
self.topup_I0 = pvs.cur_min.get()
|
||||
self.topup_dI = 0.0
|
||||
self.topup_dt = 0.0
|
||||
|
||||
def getcurr(self):
|
||||
pvs = self.pvs
|
||||
|
||||
self.cur = pvs.cur.get()
|
||||
|
||||
# def start_linac_phasefeedback(self):
|
||||
|
||||
# # injection follow bucket workaround for linac (https://elog-gfa.psi.ch/SLS/64299)
|
||||
# # Before making injection, disable the LLRF phase alarms and feedback ...
|
||||
# pvs.linac_llrf_phaseFB1.put(1)
|
||||
# pvs.linac_llrf_phaseFB2.put(1)
|
||||
|
||||
# pvs.linac_llrf_phaseFB_PBU_HIHI.put(10)
|
||||
# pvs.linac_llrf_phaseFB_FBU_HIHI.put(10)
|
||||
# pvs.linac_llrf_phaseFB_MOD1_HIHI.put(10)
|
||||
# pvs.linac_llrf_phaseFB_MOD2_HIHI.put(10)
|
||||
# pvs.linac_llrf_phaseFB_PBU_LOLO.put(-10)
|
||||
# pvs.linac_llrf_phaseFB_FBU_LOLO.put(-10)
|
||||
# pvs.linac_llrf_phaseFB_MOD1_LOLO.put(-10)
|
||||
# pvs.linac_llrf_phaseFB_MOD2_LOLO.put(-10)
|
||||
# print('phaseFB ON')
|
||||
|
||||
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)
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
pvs = self.pvs
|
||||
|
||||
# callback done
|
||||
self.CallbackFired = 0
|
||||
# (re-)load input PVs
|
||||
self.injection = pvs.injactive.get()
|
||||
self.vac = np.max([pv.get() for pv in pvs.vacs])
|
||||
self.vac_acc = np.max([pv.get() for pv in pvs.vac_acc])
|
||||
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
# here goes all the code that your service
|
||||
# is supposed to be running in any case, e.g.,
|
||||
self.getcurr()
|
||||
|
||||
# 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")
|
||||
self.trig_master = pvs.trig_master.get() and pvs.trig_secret.get()
|
||||
self.master_mode = pvs.master_mode.get()
|
||||
self.injguard = pvs.injguard.get()
|
||||
self.info = pvs.info.get().split(",")[0]
|
||||
self.tau = pvs.tau.get()
|
||||
|
||||
# get some pvs/vals
|
||||
self.val1 = self.pvs.my_pv1.get()
|
||||
self.val2 = self.pvs.my_pv2.get()
|
||||
# (re-)load control PVs
|
||||
self.vac_max = pvs.vac_max.get() * 1e-9
|
||||
self.vac_dyn = np.max(self.vac_array[:5]) * pvs.vac_dyn_frac.get()
|
||||
pvs.vac_dyn.put(self.vac_dyn * 1e9)
|
||||
|
||||
# maybe sleep to limit the update loop execution rate
|
||||
time.sleep(1)
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
# roll oldest value arr[0] to end, i.e., arr[-1]
|
||||
self.vac_array = np.roll(self.vac_array, -2)
|
||||
# replace oldest value with new value
|
||||
self.vac_array[-2:] = self.vac_acc
|
||||
|
||||
self.mbfbx_sp = int(pvs.mbfbx_gain_sp.get())
|
||||
self.mbfby_sp = int(pvs.mbfby_gain_sp.get())
|
||||
|
||||
self.vac_min = pvs.vac_min.get() * 1e-9
|
||||
self.cur_max = pvs.cur_max.get()
|
||||
self.cur_min = pvs.cur_min.get()
|
||||
self.grid_accum = pvs.grid_accum.get()
|
||||
self.grid_fpfb = pvs.grid_fpfb.get()
|
||||
self.scrubbilast = self.scrubbi
|
||||
self.scrubbi = pvs.scrubbing.get()
|
||||
|
||||
self.conditions_start_injection = ( # is any lower limit reached?
|
||||
self.vac <= self.vac_min or self.cur <= self.cur_min
|
||||
) and ( # is no upper limit exceeded ?
|
||||
self.cur <= self.cur_max and self.vac <= self.vac_max and self.vac_acc <= self.vac_dyn
|
||||
)
|
||||
|
||||
self.conditions_stop_injection = (
|
||||
# is upper current limit reached/exceeded?
|
||||
self.cur >= self.cur_max or self.vac >= self.vac_max
|
||||
)
|
||||
|
||||
self.conditions_abort_injection = (
|
||||
# is any upper limit reached/exceeded?
|
||||
self.vac_acc >= self.vac_dyn or self.cur > self.cur_max * 1.02
|
||||
# or self.injguard != 1
|
||||
)
|
||||
|
||||
def stop_injection(self):
|
||||
pvs = self.pvs
|
||||
|
||||
# disable super duper master fire trigger
|
||||
pvs.trig_firego.put(0)
|
||||
|
||||
# stop injection events
|
||||
pvs.trig_booinj.put(0)
|
||||
pvs.trig_bormp1.put(0)
|
||||
pvs.trig_bormp2.put(0)
|
||||
pvs.trig_booext.put(0)
|
||||
pvs.trig_rininj.put(0)
|
||||
|
||||
self.injection = 0
|
||||
|
||||
# prepare top-up analysis
|
||||
self.topup_I0 = self.cur
|
||||
self.topup_t0 = perf_counter()
|
||||
|
||||
# # create thread that restarts phase feedback after 2 seconds
|
||||
# if self.timerthreadready:
|
||||
|
||||
# tt = threading.Timer(10, self.start_linac_phasefeedback)
|
||||
# tt.start()
|
||||
# print('threadtimer started....')
|
||||
# self.timerthreadready = 0
|
||||
|
||||
def start_injection(self):
|
||||
pvs = self.pvs
|
||||
|
||||
# # injection follow bucket workaround for linac (https://elog-gfa.psi.ch/SLS/64299)
|
||||
# # Before making injection, disable the LLRF phase alarms and feedback ...
|
||||
# pvs.linac_llrf_phaseFB_PBU_HIHI.put(360)
|
||||
# pvs.linac_llrf_phaseFB_FBU_HIHI.put(360)
|
||||
# pvs.linac_llrf_phaseFB_MOD1_HIHI.put(360)
|
||||
# pvs.linac_llrf_phaseFB_MOD2_HIHI.put(360)
|
||||
# pvs.linac_llrf_phaseFB_PBU_LOLO.put(-360)
|
||||
# pvs.linac_llrf_phaseFB_FBU_LOLO.put(-360)
|
||||
# pvs.linac_llrf_phaseFB_MOD1_LOLO.put(-360)
|
||||
# pvs.linac_llrf_phaseFB_MOD2_LOLO.put(-360)
|
||||
# pvs.linac_llrf_phaseFB1.put(0)
|
||||
# pvs.linac_llrf_phaseFB2.put(0)
|
||||
# print('phaseFB OFF')
|
||||
# self.timerthreadready = 1
|
||||
# sleep(0.5)
|
||||
|
||||
# enable super duper master fire trigger
|
||||
pvs.trig_firego.put(1)
|
||||
|
||||
def prepare_injection(self):
|
||||
pvs = self.pvs
|
||||
|
||||
# ensure correct injector settings
|
||||
if self.cur > pvs.current_threshold.get():
|
||||
pvs.gun_grid.put(self.grid_fpfb)
|
||||
else:
|
||||
pvs.gun_grid.put(self.grid_accum)
|
||||
|
||||
self.cycles_BOIN_rb = (
|
||||
self.cycles_BOIN_sp - self.wait4warm["AGETI-CVME-MASTER-TMA:Evt-BO-Inj-EVRCount-I.VAL"]
|
||||
)
|
||||
self.cycles_BOMB_rb = (
|
||||
self.cycles_BOMB_sp - self.wait4warm["AGETI-CVME-MASTER-TMA:Evt-BO-MA-B-EVRCount-I.VAL"]
|
||||
)
|
||||
self.cycles_BOME_rb = (
|
||||
self.cycles_BOME_sp - self.wait4warm["AGETI-CVME-MASTER-TMA:Evt-BO-Ramp-EVRCount-I.VAL"]
|
||||
)
|
||||
self.cycles_BOEX_rb = (
|
||||
self.cycles_BOEX_sp - self.wait4warm["AGETI-CVME-MASTER-TMA:Evt-BO-Ext-EVRCount-I.VAL"]
|
||||
)
|
||||
self.cycles_SRIN_rb = (
|
||||
self.cycles_SRIN_sp - self.wait4warm["AGETI-CVME-MASTER-TMA:Evt-SR-Inj-EVRCount-I.VAL"]
|
||||
)
|
||||
|
||||
self.cycles_rem = np.nanmax(
|
||||
[
|
||||
self.cycles_BOIN_rb,
|
||||
self.cycles_BOMB_rb,
|
||||
self.cycles_BOME_rb,
|
||||
self.cycles_BOEX_rb,
|
||||
self.cycles_SRIN_rb,
|
||||
]
|
||||
)
|
||||
|
||||
if self.cycles_rem == self.cycles_BOIN_sp:
|
||||
pvs.trig_booinj.put(1)
|
||||
if self.cycles_rem == self.cycles_BOMB_sp:
|
||||
pvs.trig_bormp1.put(1)
|
||||
if self.cycles_rem == self.cycles_BOME_sp:
|
||||
pvs.trig_bormp2.put(1)
|
||||
if self.cycles_rem == self.cycles_BOEX_sp:
|
||||
pvs.trig_booext.put(1)
|
||||
if self.cycles_rem == self.cycles_SRIN_sp:
|
||||
pvs.trig_rininj.put(1)
|
||||
|
||||
pvs.cycles_BOIN_rb.put(self.cycles_BOIN_rb)
|
||||
pvs.cycles_BOMB_rb.put(self.cycles_BOMB_rb)
|
||||
pvs.cycles_BOME_rb.put(self.cycles_BOME_rb)
|
||||
pvs.cycles_BOEX_rb.put(self.cycles_BOEX_rb)
|
||||
pvs.cycles_SRIN_rb.put(self.cycles_SRIN_rb)
|
||||
|
||||
def start_scrubbing(self):
|
||||
pvs = self.pvs
|
||||
|
||||
if self.scrubbi == 1:
|
||||
pvs.trig_pinger_v.put(1)
|
||||
elif self.scrubbi == 2:
|
||||
pvs.trig_pinger_h.put(1)
|
||||
elif self.scrubbi == 3:
|
||||
pvs.trig_pinger_v.put(1)
|
||||
pvs.trig_pinger_h.put(1)
|
||||
elif self.scrubbi == 4:
|
||||
for i in range(self.mbfby_sp):
|
||||
pvs.mbfby_gain.put(i + 1)
|
||||
sleep(0.05)
|
||||
elif self.scrubbi == 5:
|
||||
for i in range(self.mbfbx_sp):
|
||||
pvs.mbfbx_gain.put(i + 1)
|
||||
sleep(0.05)
|
||||
elif self.scrubbi == 6:
|
||||
for i in range(self.mbfby_sp):
|
||||
pvs.mbfby_gain.put(i + 1)
|
||||
sleep(0.05)
|
||||
for i in range(self.mbfbx_sp):
|
||||
pvs.mbfbx_gain.put(i + 1)
|
||||
sleep(0.05)
|
||||
elif self.scrubbi == 7:
|
||||
for i in range(self.mbfby_sp):
|
||||
pvs.mbfby_gain.put(i + 1)
|
||||
sleep(0.05)
|
||||
pvs.mbfby_sweep_onoff.put(1)
|
||||
|
||||
print("scrubbing started...")
|
||||
|
||||
def stop_scrubbing(self):
|
||||
pvs = self.pvs
|
||||
|
||||
pvs.mbfbx_gain.put(0)
|
||||
pvs.mbfby_gain.put(0)
|
||||
pvs.trig_pinger_h.put(0)
|
||||
pvs.trig_pinger_v.put(0)
|
||||
|
||||
if self.scrubbi == 7:
|
||||
self.mbfby_sweepfreq_rb = np.round(pvs.mbfby_sweepfreq_rb.get())
|
||||
pvs.mbfby_sweep_onoff.put(0)
|
||||
pvs.mbfby_sweepfreq_start.put(self.mbfby_sweepfreq_rb)
|
||||
pvs.mbfby_freq_sp.put(self.mbfby_sweepfreq_rb)
|
||||
|
||||
def pause_scrubbing(self):
|
||||
pvs = self.pvs
|
||||
|
||||
if self.scrubbi > 0:
|
||||
pvs.mbfbx_gain.put(0)
|
||||
pvs.mbfby_gain.put(0)
|
||||
pvs.trig_pinger_h.put(0)
|
||||
pvs.trig_pinger_v.put(0)
|
||||
|
||||
if self.scrubbi == 7:
|
||||
self.mbfby_sweepfreq_rb = np.round(pvs.mbfby_sweepfreq_rb.get())
|
||||
pvs.mbfby_sweep_onoff.put(0)
|
||||
pvs.mbfby_sweepfreq_start.put(self.mbfby_sweepfreq_rb)
|
||||
pvs.mbfby_freq_sp.put(self.mbfby_sweepfreq_rb)
|
||||
|
||||
def set_status(self, status, wait=0):
|
||||
pvs = self.pvs
|
||||
|
||||
if self.info != status:
|
||||
self.info = status
|
||||
|
||||
if wait == 0:
|
||||
pvs.info.put(status)
|
||||
|
||||
if wait > 0:
|
||||
pvs.info.put(status + ", Wait {:.0f}s".format(wait))
|
||||
|
||||
## define some "static" class attributes, i.e., persistent over every class call v = vals()
|
||||
# trigger for execution of loop (controlled by PV callback)
|
||||
ChangeTrue = 0
|
||||
# current state of callback
|
||||
CallbackActive = 0
|
||||
# start time for next loop firing, if no vacuum changes
|
||||
tl = perf_counter()
|
||||
# waittime before injection after preparation
|
||||
tp = perf_counter()
|
||||
# status for GUI message
|
||||
status = "Service Restarted"
|
||||
# injection abort status
|
||||
abort = 0
|
||||
# injection abort timer
|
||||
abort_t0 = perf_counter()
|
||||
# states of state machine
|
||||
states = {
|
||||
"abort": "Injection Abort",
|
||||
"decay + scrub": "Decaying Beam, Scrub ON",
|
||||
"decay - scrub": "Decaying Beam, Scrub OFF",
|
||||
"inject": "Injecting",
|
||||
"wait 4 master": "Waiting for Master Trigger",
|
||||
"wait 4 manual": "Master Manual Mode Required",
|
||||
"warmup": "Top Up Save Warmup",
|
||||
"pause": "Service Paused",
|
||||
}
|
||||
|
||||
# define callback function to trigger loop execution on change of corresponding pv change
|
||||
def InjectionCallback(self, pvname=None, value=None, char_value=None, **kw):
|
||||
# trigger execution of loop with actual callback since execution of code here not reliable
|
||||
self.ChangeTrue += 1
|
||||
|
||||
def WarmupCallback(self, pvname=None, value=None, char_value=None, **kw):
|
||||
# trigger execution of loop with actual callback since execution of code here not reliable
|
||||
self.wait4warm[pvname] += 1
|
||||
self.ChangeTrue += 1
|
||||
|
||||
@@ -4,6 +4,5 @@ from agebd_scrubbing import PVs, Service
|
||||
def test_app():
|
||||
service_name = "DUMMY"
|
||||
pvs = PVs(service_name=service_name)
|
||||
service = Service(name=service_name, pvs=pvs)
|
||||
service.CallbackFired = 1
|
||||
service = Service(pvs=pvs)
|
||||
service.update()
|
||||
|
||||
Generated
+5
-1
@@ -30,6 +30,7 @@ version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "agebd" },
|
||||
{ name = "numpy" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
@@ -40,7 +41,10 @@ dev = [
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "agebd", editable = "../../../../packages/agebd" }]
|
||||
requires-dist = [
|
||||
{ name = "agebd", editable = "../../../../packages/agebd" },
|
||||
{ name = "numpy", specifier = ">=2.2.6" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
|
||||
Reference in New Issue
Block a user