From af0f9861c060681dada00ffc66b0b7e36cbb8702 Mon Sep 17 00:00:00 2001 From: Max Rakitin Date: Tue, 14 May 2024 18:46:21 -0400 Subject: [PATCH] Dev updates for the Zebra IOC --- scripts/run-caproto-ioc.sh | 5 +- scripts/run-caproto-zebra-ioc.sh | 7 ++ src/srx_caproto_iocs/base.py | 7 +- src/srx_caproto_iocs/zebra/caproto_ioc.py | 119 +++++++++++++++++++++- 4 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 scripts/run-caproto-zebra-ioc.sh diff --git a/scripts/run-caproto-ioc.sh b/scripts/run-caproto-ioc.sh index 9cfaefe..808b8dd 100644 --- a/scripts/run-caproto-ioc.sh +++ b/scripts/run-caproto-ioc.sh @@ -3,7 +3,8 @@ set -vxeuo pipefail CAPROTO_IOC="${1:-srx_caproto_iocs.base}" - +DEFAULT_PREFIX="BASE:{{Dev:Save1}}:" +CAPROTO_IOC_PREFIX="${2:-${DEFAULT_PREFIX}}" # shellcheck source=/dev/null if [ -f "/etc/profile.d/epics.sh" ]; then . /etc/profile.d/epics.sh @@ -12,4 +13,4 @@ fi export EPICS_CAS_AUTO_BEACON_ADDR_LIST="no" export EPICS_CAS_BEACON_ADDR_LIST="${EPICS_CA_ADDR_LIST:-127.0.0.255}" -python -m "${CAPROTO_IOC}" --prefix="BASE:{{Dev:Save1}}:" --list-pvs +python -m "${CAPROTO_IOC}" --prefix="${CAPROTO_IOC_PREFIX}" --list-pvs diff --git a/scripts/run-caproto-zebra-ioc.sh b/scripts/run-caproto-zebra-ioc.sh new file mode 100644 index 0000000..1d4118c --- /dev/null +++ b/scripts/run-caproto-zebra-ioc.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -vxeuo pipefail + +SCRIPT_DIR="$(dirname "$0")" + +bash "${SCRIPT_DIR}/run-caproto-ioc.sh" srx_caproto_iocs.zebra.caproto_ioc "XF:05IDD-ES:1{{Dev:Zebra2}}:" diff --git a/src/srx_caproto_iocs/base.py b/src/srx_caproto_iocs/base.py index 570794d..6c3152b 100644 --- a/src/srx_caproto_iocs/base.py +++ b/src/srx_caproto_iocs/base.py @@ -104,8 +104,7 @@ async def queue(self, instance, async_lib): ) thread.start() - @stage.putter - async def stage(self, instance, value): + async def _stage(self, instance, value): """The stage method to perform preparation of a dataset to save the data.""" if ( instance.value in [True, StageStates.STAGED.value] @@ -150,6 +149,10 @@ async def stage(self, instance, value): return False + @stage.putter + async def stage(self, *args, **kwargs): + return await self._stage(*args, **kwargs) + def _get_current_dataset(self, frame): """The method to return a desired dataset. diff --git a/src/srx_caproto_iocs/zebra/caproto_ioc.py b/src/srx_caproto_iocs/zebra/caproto_ioc.py index f2ae288..5970327 100644 --- a/src/srx_caproto_iocs/zebra/caproto_ioc.py +++ b/src/srx_caproto_iocs/zebra/caproto_ioc.py @@ -1,20 +1,137 @@ from __future__ import annotations import textwrap +from pprint import pformat +from caproto.asyncio.client import Context from caproto.server import run, template_arg_parser from ..base import CaprotoSaveIOC, check_args +from ..utils import now + +# def export_nano_zebra_data(zebra, filepath, fastaxis): +# j = 0 +# while zebra.pc.data_in_progress.get() == 1: +# print("Waiting for zebra...") +# ttime.sleep(0.1) +# j += 1 +# if j > 10: +# print("THE ZEBRA IS BEHAVING BADLY CARRYING ON") +# break + +# time_d = zebra.pc.data.time.get() +# enc1_d = zebra.pc.data.enc1.get() +# enc2_d = zebra.pc.data.enc2.get() +# enc3_d = zebra.pc.data.enc3.get() + +# px = zebra.pc.pulse_step.get() +# if fastaxis == 'NANOHOR': +# # Add half pixelsize to correct encoder +# enc1_d = enc1_d + (px / 2) +# elif fastaxis == 'NANOVER': +# # Add half pixelsize to correct encoder +# enc2_d = enc2_d + (px / 2) +# elif fastaxis == 'NANOZ': +# # Add half pixelsize to correct encoder +# enc3_d = enc3_d + (px / 2) + +# size = (len(time_d),) +# with h5py.File(filepath, "w") as f: +# dset0 = f.create_dataset("zebra_time", size, dtype="f") +# dset0[...] = np.array(time_d) +# dset1 = f.create_dataset("enc1", size, dtype="f") +# dset1[...] = np.array(enc1_d) +# dset2 = f.create_dataset("enc2", size, dtype="f") +# dset2[...] = np.array(enc2_d) +# dset3 = f.create_dataset("enc3", size, dtype="f") +# dset3[...] = np.array(enc3_d) + + +# class ZebraPositionCaptureData(Device): +# """ +# Data arrays for the Zebra position capture function and their metadata. +# """ +# # Data arrays +# ... +# enc1 = Cpt(EpicsSignal, "PC_ENC1") # XF:05IDD-ES:1{Dev:Zebra2}:PC_ENC1 +# enc2 = Cpt(EpicsSignal, "PC_ENC2") # XF:05IDD-ES:1{Dev:Zebra2}:PC_ENC2 +# enc3 = Cpt(EpicsSignal, "PC_ENC3") # XF:05IDD-ES:1{Dev:Zebra2}:PC_ENC3 +# time = Cpt(EpicsSignal, "PC_TIME") # XF:05IDD-ES:1{Dev:Zebra2}:PC_TIME +# ... + +# class ZebraPositionCapture(Device): +# """ +# Signals for the position capture function of the Zebra +# """ + +# # Configuration settings and status PVs +# ... +# pulse_step = Cpt(EpicsSignalWithRBV, "PC_PULSE_STEP") # XF:05IDD-ES:1{Dev:Zebra2}:PC_PULSE_STEP +# ... +# data_in_progress = Cpt(EpicsSignalRO, "ARRAY_ACQ") # XF:05IDD-ES:1{Dev:Zebra2}:ARRAY_ACQ +# ... +# data = Cpt(ZebraPositionCaptureData, "") + +# nanoZebra = SRXZebra( +# "XF:05IDD-ES:1{Dev:Zebra2}:", name="nanoZebra", +# read_attrs=["pc.data.enc1", "pc.data.enc2", "pc.data.enc3", "pc.data.time"], +# ) class ZebraSaveIOC(CaprotoSaveIOC): """Zebra caproto save IOC.""" + # enc1 = Cpt(EpicsSignal, "PC_ENC1") + # enc2 = Cpt(EpicsSignal, "PC_ENC2") + # enc3 = Cpt(EpicsSignal, "PC_ENC3") + # enc4 = Cpt(EpicsSignal, "PC_ENC4") + + # data_in_progress = pvproperty() # + + # time_d = pvproperty() + # enc1_d = pvproperty() + # enc2_d = pvproperty() + # enc3_d = pvproperty() + # pulse_step = pvproperty() + + def __init__(self, *args, external_pvs=None, **kwargs): + """Init method. + + external_pvs : dict + a dictionary of external PVs with keys as human-readable names. + """ + super().__init__(*args, **kwargs) + self._external_pvs = external_pvs + + async def _stage(self, *args, **kwargs): + await super()._stage(*args, **kwargs) + client_context = Context() + res = {} + if self._external_pvs is not None: + pvobjects = await client_context.get_pvs(*self._external_pvs.values()) + for i, (name, pv) in enumerate(self._external_pvs.items()): # noqa: B007 + pvobject = pvobjects[i] + # print(f"{now()}: {pvobject = }") + ret = await pvobject.read() + # print(f"{now()}: {val.data}") + res[name] = {"data": ret.data, "shape": ret.data.shape} + print(f"{now()}:\n{pformat(res)}") + return True + if __name__ == "__main__": parser, split_args = template_arg_parser( default_prefix="", desc=textwrap.dedent(ZebraSaveIOC.__doc__) ) ioc_options, run_options = check_args(parser, split_args) - ioc = ZebraSaveIOC(**ioc_options) # TODO: pass libca IOC PVs of interest + + external_pvs = { + "pulse_step": "XF:05IDD-ES:1{Dev:Zebra2}:PC_PULSE_STEP", + "data_in_progress": "XF:05IDD-ES:1{Dev:Zebra2}:ARRAY_ACQ", + "enc1": "XF:05IDD-ES:1{Dev:Zebra2}:PC_ENC1", + "enc2": "XF:05IDD-ES:1{Dev:Zebra2}:PC_ENC2", + "enc3": "XF:05IDD-ES:1{Dev:Zebra2}:PC_ENC3", + "time": "XF:05IDD-ES:1{Dev:Zebra2}:PC_TIME", + } + + ioc = ZebraSaveIOC(external_pvs=external_pvs, **ioc_options) run(ioc.pvdb, **run_options)