feature: add new service cool-service
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
[project]
|
||||
name = "agebd-cool-service"
|
||||
version = "0.1.0"
|
||||
description = "AGEBD-COOL-SERVICE Service"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"agebd==1.2.0",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8.4.2",
|
||||
"ruff>=0.15.20",
|
||||
]
|
||||
|
||||
# This tells uv where to look for 'agebd' during LOCAL dev work
|
||||
[tool.uv.sources]
|
||||
# Path is relative to where agebd package is deployed on hla
|
||||
# machines (see CI/CD in .gitea/workflows)
|
||||
agebd = { path = "../../../../../packages/{{ agebd_env }}/agebd", editable = true }
|
||||
|
||||
[tool.ruff]
|
||||
include = [
|
||||
"src/**/*.py",
|
||||
]
|
||||
line-length = 100
|
||||
|
||||
[tool.ruff.lint]
|
||||
# use isort to sort imports
|
||||
extend-select = ["I"]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
known-first-party = ["agebd"]
|
||||
@@ -0,0 +1,108 @@
|
||||
from time import sleep
|
||||
|
||||
import typer
|
||||
from epics import dbr
|
||||
|
||||
from agebd.enums import LogLevel
|
||||
from agebd.pv import LocalPVLink
|
||||
from agebd.pv import PVLink as PV
|
||||
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
|
||||
|
||||
__version__ = printgetversion(__file__)
|
||||
|
||||
PV = get_pv_class()
|
||||
|
||||
|
||||
class PVs(BasePVs):
|
||||
# Option 1:
|
||||
## Service specific PVs from dedicated IOC
|
||||
my_pv1 = PV("MY_PV1")
|
||||
|
||||
## Service specific PVs from other IOCs
|
||||
# ...
|
||||
|
||||
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("MY_PV2")
|
||||
|
||||
## 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("...", auto_monitor=dbr.DBE_VALUE)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
# TODO: What is this for?
|
||||
self.path = "sls/bd/bin"
|
||||
|
||||
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
|
||||
sleep(1)
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
|
||||
# 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
|
||||
self.pvs.my_pv1.put("MY_PV1 value")
|
||||
self.pvs.my_pv2.put("MY_PV2 value")
|
||||
self.val1 = self.pvs.my_pv1.get()
|
||||
self.val2 = self.pvs.my_pv2.get()
|
||||
print(self.val1)
|
||||
print(self.val2)
|
||||
# maybe sleep to limit the update loop execution rate
|
||||
sleep(1)
|
||||
#######################################################################################
|
||||
#######################################################################################
|
||||
|
||||
|
||||
def main(
|
||||
log_level: LogLevel = typer.Option(
|
||||
LogLevel.INFO,
|
||||
"--log-level",
|
||||
"-l",
|
||||
# Looks at the OS env variable first. If empty, falls back to "INFO"
|
||||
envvar="SLS_SERVICE_LOG_LEVEL",
|
||||
# help="Set log level: DEBUG, INFO, WARNING, ERROR, CRITICAL",
|
||||
case_sensitive=False,
|
||||
),
|
||||
):
|
||||
init_logging(log_level)
|
||||
service_name = ""
|
||||
pvs = PVs(service_name=service_name, pv_factory=PV)
|
||||
service = Service(name=service_name, pvs=pvs)
|
||||
runner = CallbackRunner(service=service)
|
||||
runner.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
typer.run(main)
|
||||
@@ -0,0 +1,4 @@
|
||||
file COOL-SERVICE.template {
|
||||
pattern { DEVICE }
|
||||
{ AGEBD-COOL-SERVICE }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
cpu_architecture: x86_64
|
||||
epics_version: 7.0.8
|
||||
ioc_host:
|
||||
ioc_port: 9999
|
||||
os: RHEL8
|
||||
os_id: rhel
|
||||
@@ -0,0 +1,27 @@
|
||||
# This is a template for preaparing an IOC providing BD Service specific PVs
|
||||
# Access Security Groups:
|
||||
# DEFAULT - logt wenn EGU oder so veraendert werden
|
||||
# LOG - logt wenn neuer wert geschrieben wird
|
||||
# PROTECTED - verhindert versuchte caputs auf alle fields ausser VAL
|
||||
# READONLY - verhindert versuchte caputs und aktiviert caputlog
|
||||
# Input links can specify CA, CP, or CPP.
|
||||
# If the input link specifies CA, it will be forced to be a Channel Access link.
|
||||
# If the input link specifies CP, it will also be forced to be a Channel Access link; in addition, it will cause the record containing the input link to process whenever a monitor is posted, no matter what the record's SCAN field specifies.
|
||||
# If the input link specifies CPP, it means the same thing as CP, except that the record will be processed if and only if the record itself specifies passive in its SCAN field. Output links can specify CA, which will simply cause them to be Channel Access links.
|
||||
#
|
||||
|
||||
record(ao, "$(DEVICE):AO") {
|
||||
field(DESC, "Analog Output Record (ao)")
|
||||
field(ASG, "PROTECTED")
|
||||
field(VAL, "0")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
record(bo, "$(DEVICE):BO") {
|
||||
field(DESC, "Binary Output Record (bo)")
|
||||
field(ASG, "READONLY")
|
||||
field(ZSV, "MINOR")
|
||||
field(OSV, "NO_ALARM")
|
||||
field(VAL, "1")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
epicsEnvSet("ENGINEER", "")
|
||||
@@ -0,0 +1 @@
|
||||
# TODO
|
||||
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=AGEBD-SERVICE-COOL-SERVICE
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Environment=AGEBD_ENV={{ agebd_env }}
|
||||
ExecStart=/sls/bd/hla/bin/start_service.sh 004 COOL-SERVICE
|
||||
estart=no
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
# https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable /sls/bd/bin/systemd/AGEBD-SERVICE-COOL-SERVICE.service
|
||||
# systemctl --user start AGEBD-SERVICE-COOL-SERVICE
|
||||
Reference in New Issue
Block a user