feat: migrate shifttool python service
This commit is contained in:
@@ -6,6 +6,7 @@ requires-python = "==3.10.*"
|
||||
dependencies = [
|
||||
"agebd",
|
||||
]
|
||||
# TODO: elog
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
|
||||
@@ -5,9 +5,9 @@ from agebd.runner import CallbackRunner
|
||||
from agebd.utils import init_logging
|
||||
from agebd_shifttool import PVs, Service
|
||||
|
||||
|
||||
# TODO: remove: cicd test 10
|
||||
|
||||
|
||||
def main(
|
||||
log_level: LogLevel = typer.Option(
|
||||
LogLevel.INFO,
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import locale
|
||||
import time
|
||||
from datetime import datetime
|
||||
from time import time
|
||||
|
||||
from epics import dbr
|
||||
import elog
|
||||
|
||||
from agebd.pv import LocalPVLink
|
||||
from agebd.pv import get_pv_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
|
||||
|
||||
# Setze die Locale auf Deutsch (z. B. 'de_DE' oder 'de_CH' für Schweiz)
|
||||
locale.setlocale(locale.LC_TIME, "de_CH.UTF-8") # oder 'de_CH.UTF-8'
|
||||
|
||||
__version__ = printgetversion(__file__)
|
||||
|
||||
@@ -14,36 +19,147 @@ PV = get_pv_class()
|
||||
|
||||
|
||||
class PVs(BasePVs):
|
||||
# Option 1:
|
||||
## Service specific PVs from dedicated IOC
|
||||
my_pv1 = PV("AGEBD-SHIFTTOOL:AO") # TODO: is this a good PV to use?
|
||||
req_MakeElog = PV("AGEOP-SHIFTTOOL:REQ-SHIFT-ENTRY.VAL")
|
||||
req_UpdateTeam = PV("AGEOP-SHIFTTOOL:REQ-UPDATE-TEAM.VAL")
|
||||
|
||||
## Service specific PVs from other IOCs
|
||||
# ...
|
||||
shift = PV("AGEOP-SHIFTTOOL:TXT-SHIFT.VAL")
|
||||
crew = PV("AGEOP-SHIFTTOOL:TXT-SHIFT-CREW.VAL")
|
||||
|
||||
operators = [
|
||||
PV("AGEOP-SHIFTTOOL:TXT-OPERATOR-01.VAL"),
|
||||
PV("AGEOP-SHIFTTOOL:TXT-OPERATOR-02.VAL"),
|
||||
PV("AGEOP-SHIFTTOOL:TXT-OPERATOR-03.VAL"),
|
||||
PV("AGEOP-SHIFTTOOL:TXT-OPERATOR-04.VAL"),
|
||||
PV("AGEOP-SHIFTTOOL:TXT-OPERATOR-05.VAL"),
|
||||
]
|
||||
links = {
|
||||
"Problem-01": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Problem-01.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Problem&pAutor=Rast%2C%20Streich%2C%20Weber&pEffekt=Downtime",
|
||||
],
|
||||
"Problem-02": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Problem-02.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Problem&pAutor=Rast%2C%20Streich%2C%20Weber&pEffekt=Beamdrop",
|
||||
],
|
||||
"Problem-03": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Problem-03.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Problem&pAutor=Rast%2C%20Streich%2C%20Weber&pEffekt=Lifetime",
|
||||
],
|
||||
"Problem-04": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Problem-04.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Problem&pAutor=Rast%2C%20Streich%2C%20Weber&pEffekt=Beamsize",
|
||||
],
|
||||
"Problem-05": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Problem-05.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Problem&pAutor=Rast%2C%20Streich%2C%20Weber&pEffekt=keiner",
|
||||
],
|
||||
"Problem-06": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Problem-06.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Problem&pAutor=Rast%2C%20Streich%2C%20Weber&pEffekt=anderer",
|
||||
],
|
||||
"Optimierung-01": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Optimierung-01.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Tuning&pAutor=Rast%2C%20Streich%2C%20Weber&pTitel=Optimierung%20Transfer",
|
||||
],
|
||||
"Optimierung-02": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Optimierung-02.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Tuning&pAutor=Rast%2C%20Streich%2C%20Weber&pTitel=Optimierung%20Tau%2FYsig",
|
||||
],
|
||||
"Optimierung-03": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Optimierung-03.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Tuning&pAutor=Rast%2C%20Streich%2C%20Weber&pTitel=Optimierung",
|
||||
],
|
||||
"Optimierung-04": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Optimierung-04.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Tuning&pAutor=Rast%2C%20Streich%2C%20Weber&pTitel=Optimierung",
|
||||
],
|
||||
"Optimierung-05": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Optimierung-05.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Tuning&pAutor=Rast%2C%20Streich%2C%20Weber&pTitel=Optimierung",
|
||||
],
|
||||
"Optimierung-06": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Optimierung-06.VAL"),
|
||||
"https://elog-gfa.psi.ch/sls/?cmd=Neu&pEintrag=Tuning&pAutor=Rast%2C%20Streich%2C%20Weber&pTitel=Optimierung",
|
||||
],
|
||||
"Piquet": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Piquet.VAL"),
|
||||
],
|
||||
"Info": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-Info.VAL"),
|
||||
],
|
||||
"AktuellesProtokoll": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-AktuellesProtokoll.VAL"),
|
||||
],
|
||||
"AndereEintraege-01": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-AndereEintraege-01.VAL"),
|
||||
],
|
||||
"AndereEintraege-02": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-AndereEintraege-02.VAL"),
|
||||
],
|
||||
"AndereEintraege-03": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-AndereEintraege-03.VAL"),
|
||||
],
|
||||
"ListenAnzeigen-01": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-ListenAnzeigen-01.VAL"),
|
||||
],
|
||||
"ListenAnzeigen-02": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-ListenAnzeigen-02.VAL"),
|
||||
],
|
||||
"ListenAnzeigen-03": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-ListenAnzeigen-03.VAL"),
|
||||
],
|
||||
"ListenAnzeigen-04": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-ListenAnzeigen-04.VAL"),
|
||||
],
|
||||
"PiquetWoche": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-PiquetWoche.VAL"),
|
||||
],
|
||||
"OffeneAuftraege": [
|
||||
PV("AGEOP-SHIFTTOOL:LINK-OffeneAuftraege.VAL"),
|
||||
],
|
||||
}
|
||||
|
||||
minisave = [
|
||||
PV("AGEBD-PARAMS:LIFETIME.VAL"),
|
||||
PV("AGEBD-DBPM3CURR:CURRENT-AVG.VAL"),
|
||||
PV("AGEBD-PARAMS:INJECTION-RATE.VAL"),
|
||||
PV("AGEBD-DBPM3CURR:ACCUMULATED-BEAM-DOSE.VAL"),
|
||||
PV("AGEOP-CPCL-TRANSMISSION:ICT-BRTL_2_BPM-RING.VAL"),
|
||||
PV("ARSGE-CECL-FOFB1:X-RMS-UM.VAL"),
|
||||
PV("ARSGE-CECL-FOFB1:Y-RMS-UM.VAL"),
|
||||
PV("X01DD-FE-ABS1:STATUS.VAL"),
|
||||
PV("X08DB-FE-ABS1:STATUS.VAL"),
|
||||
PV("X01DA-FE-ABS1:STATUS.VAL"),
|
||||
PV("X01DA-OP-PSYS:SH-A-OPEN.VAL"),
|
||||
PV("X02DA-FE-ABS1:STATUS.VAL"),
|
||||
PV("X02DA-OP-PSYS:SH-A-OPEN.VAL"),
|
||||
PV("X06DA-FE-ABS1:STATUS.VAL"),
|
||||
PV("X06DA-OP-PSYS:SH-A-OPEN.VAL"),
|
||||
PV("X07DA-FE-ABS1:STATUS.VAL"),
|
||||
PV("X07D-OP-PSYS:SH-A-OPEN.VAL"),
|
||||
PV("X07DB-FE-ABS1:STATUS.VAL"),
|
||||
PV("X07D-OP-PSYS:SH-B-OPEN.VAL"),
|
||||
PV("X10DA-FE-ABS1:STATUS.VAL"),
|
||||
PV("X10DA-OP-PSYS:SH-A-OPEN.VAL"),
|
||||
PV("X04SA-UIND:GAP-READ.VAL"),
|
||||
PV("X04SA-UIND:SCTRL.VAL"),
|
||||
PV("X06SA-UIND:GAP-READ.VAL"),
|
||||
PV("X06SA-UIND:SCTRL.VAL"),
|
||||
PV("X09LB-UIND:GAP-READ.VAL"),
|
||||
PV("X09LB-UIND:SCTRL.VAL"),
|
||||
PV("X10SA-UIND:GAP-READ.VAL"),
|
||||
PV("X10SA-UIND:SCTRL.VAL"),
|
||||
PV("X12SA-UIND:GAP-READ.VAL"),
|
||||
PV("X12SA-UIND:SCTRL.VAL"),
|
||||
]
|
||||
|
||||
NShiftTasks = PV("AGEOP-SHIFTTOOL:CNT-TODO.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-SHIFTTOOL: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-SHIFTTOOL:CALLBACK", auto_monitor=dbr.DBE_VALUE
|
||||
) # TODO: ok?
|
||||
self.CallbackPV = PV("AGEOP-SHIFTTOOL:REQ-CALC.VAL")
|
||||
|
||||
|
||||
class Service(BaseService[PVs]):
|
||||
@@ -56,38 +172,98 @@ class Service(BaseService[PVs]):
|
||||
):
|
||||
super().__init__(name, pvs, version, sleep_interval)
|
||||
|
||||
# TODO: What is this for?
|
||||
self.path = "sls/bd/bin"
|
||||
# FACILITY = SLS
|
||||
self.logbook = elog.open(
|
||||
"https://elog-gfa.psi.ch/SLS/",
|
||||
user="robot",
|
||||
password="robot",
|
||||
use_ssl=True,
|
||||
)
|
||||
self.path = "/sls/op/data/applications/minisave/"
|
||||
|
||||
def minisave(self):
|
||||
|
||||
# table overview of current machine and beamline parameters
|
||||
|
||||
return
|
||||
|
||||
def update(self):
|
||||
# when callback is fired, this code will be executed
|
||||
pvs = self.pvs
|
||||
|
||||
if self.CallbackFired:
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
# here goes all the code that your service
|
||||
# is supposed to be running when the callback is fired
|
||||
time.sleep(1)
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
self.req_UpdateTeam = pvs.req_UpdateTeam.get()
|
||||
self.req_MakeElog = pvs.req_MakeElog.get()
|
||||
print(self.req_UpdateTeam, self.req_MakeElog)
|
||||
|
||||
# if shift crew update request detected update current shift crew
|
||||
if self.req_UpdateTeam or self.req_MakeElog:
|
||||
pvs.req_UpdateTeam.put(0)
|
||||
print("Team aktualisieren")
|
||||
|
||||
self.logtxt = []
|
||||
self.logtxt.append("Eingetragene Mannschaft:")
|
||||
self.date = datetime.now()
|
||||
self.hour = int(self.date.strftime("%H"))
|
||||
|
||||
# Format the date
|
||||
self.day_abbr = self.date.strftime(
|
||||
"%a"
|
||||
) # Abbreviated weekday (e.g., "Mi" for Mittwoch)
|
||||
self.date_str = self.date.strftime(
|
||||
"%d-%b-%Y"
|
||||
) # Day-Month-Year (e.g., "17-Jun-2026")
|
||||
self.week_num = self.date.isocalendar()[1] # ISO calendar week number (e.g., 25)
|
||||
self.timestamp = f"{self.day_abbr} {self.date_str} KW{self.week_num}"
|
||||
|
||||
if self.hour > 4 and self.hour < 8:
|
||||
self.shift = "Früh-Schicht"
|
||||
elif self.hour > 12 and self.hour < 16:
|
||||
self.shift = "Spät-Schicht"
|
||||
elif self.hour > 20:
|
||||
self.shift = "Nacht-Schicht"
|
||||
else:
|
||||
self.shift = "Verschlafen"
|
||||
|
||||
self.shift += ", " + self.timestamp
|
||||
pvs.shift.put(self.shift)
|
||||
|
||||
self.operators = []
|
||||
for op in pvs.operators:
|
||||
op = op.get(as_string=True)
|
||||
if len(op) > 1:
|
||||
self.operators.append(op)
|
||||
|
||||
print(self.operators)
|
||||
self.crew = ", ".join(self.operators)
|
||||
pvs.crew.put(self.crew)
|
||||
self.logtxt.append(self.crew)
|
||||
print(self.crew)
|
||||
|
||||
# if shift entry request detected and current shift entry not yet made, make it
|
||||
if self.req_MakeElog:
|
||||
pvs.req_MakeElog.put(0)
|
||||
print("Schicht eintragen")
|
||||
|
||||
# Write to elog
|
||||
self.logtxt = "".join(self.logtxt)
|
||||
|
||||
self.dict_of_attributes = {
|
||||
"Wann": time(),
|
||||
"Autor": self.crew,
|
||||
"Eintrag": "Schicht-Übergabe",
|
||||
"Titel": self.shift,
|
||||
"Encoding": "html",
|
||||
}
|
||||
|
||||
print(" ...elog entry prepared...")
|
||||
|
||||
self.msg_id = self.logbook.post(
|
||||
self.logtxt,
|
||||
attributes=self.dict_of_attributes,
|
||||
suppress_email_notification=True,
|
||||
attribute_as_param="value",
|
||||
)
|
||||
print(" ...elog entry submitted...")
|
||||
|
||||
# callback done
|
||||
self.CallbackFired = 0
|
||||
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
# 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()
|
||||
|
||||
# maybe sleep to limit the update loop execution rate
|
||||
time.sleep(1)
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
|
||||
Reference in New Issue
Block a user