diff --git a/csaxs_bec/devices/epics/eps.py b/csaxs_bec/devices/epics/eps.py index d7678f5..68ba3a3 100644 --- a/csaxs_bec/devices/epics/eps.py +++ b/csaxs_bec/devices/epics/eps.py @@ -52,8 +52,8 @@ class EPSAlarms(EPSSubDevices): ) -class Valves(EPSSubDevices): - """Valves at the cSAXS beamline.""" +class ValvesFrontend(EPSSubDevices): + """Valves frontend at the cSAXS beamline.""" ################ ### Frontend ### @@ -64,7 +64,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="fevvpg0000", kind=Kind.omitted, - doc="Frontend valve FE-VVPG-0000", + doc="FE-VVPG-0000", auto_monitor=True, labels={"valve"}, ) @@ -74,7 +74,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="fevvpg1010", kind=Kind.omitted, - doc="Frontend valve FE-VVPG-1010", + doc="FE-VVPG-1010", auto_monitor=True, labels={"valve"}, ) @@ -84,7 +84,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="fevvfv2010", kind=Kind.omitted, - doc="Frontend valve FE-VVFV-2010", + doc="FE-VVFV-2010", auto_monitor=True, labels={"valve"}, ) @@ -94,7 +94,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="fevvpg2010", kind=Kind.omitted, - doc="Frontend valve FE-VVPG-2010", + doc="FE-VVPG-2010", auto_monitor=True, labels={"valve"}, ) @@ -103,13 +103,17 @@ class Valves(EPSSubDevices): ### Optics ### ################ + +class ValvesOptics(EPSSubDevices): + """Valves at the optics hutch.""" + op_vvpg_1010 = Cpt( EpicsSignalRO, read_pv="X12SA-OP-VVPG-1010:PLC_OPEN", add_prefix=("",), name="opvvpg1010", kind=Kind.omitted, - doc="Optics valve OP-VVPG-1010", + doc="OP-VVPG-1010", auto_monitor=True, labels={"valve"}, ) @@ -119,7 +123,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="opvvpg2010", kind=Kind.omitted, - doc="Optics valve OP-VVPG-2010", + doc="OP-VVPG-2010", auto_monitor=True, labels={"valve"}, ) @@ -129,7 +133,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="opvvpg3010", kind=Kind.omitted, - doc="Optics valve OP-VVPG-3010", + doc="OP-VVPG-3010", auto_monitor=True, labels={"valve"}, ) @@ -139,7 +143,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="opvvpg3020", kind=Kind.omitted, - doc="Optics valve OP-VVPG-3020", + doc="OP-VVPG-3020", auto_monitor=True, labels={"valve"}, ) @@ -149,7 +153,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="opvvpg4010", kind=Kind.omitted, - doc="Optics valve OP-VVPG-4010", + doc="OP-VVPG-4010", auto_monitor=True, labels={"valve"}, ) @@ -159,7 +163,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="opvvpg5010", kind=Kind.omitted, - doc="Optics valve OP-VVPG-5010", + doc="OP-VVPG-5010", auto_monitor=True, labels={"valve"}, ) @@ -169,7 +173,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="opvvpg6010", kind=Kind.omitted, - doc="Optics valve OP-VVPG-6010", + doc="OP-VVPG-6010", auto_monitor=True, labels={"valve"}, ) @@ -179,7 +183,7 @@ class Valves(EPSSubDevices): add_prefix=("",), name="opvvpg7010", kind=Kind.omitted, - doc="Optics valve OP-VVPG-7010", + doc="OP-VVPG-7010", auto_monitor=True, labels={"valve"}, ) @@ -188,20 +192,24 @@ class Valves(EPSSubDevices): ### Endstation ### ################## + +class ValvesEndstation(EPSSubDevices): + """Endstation valves at the cSAXS beamline.""" + es_vvpg_1010 = Cpt( EpicsSignalRO, read_pv="X12SA-ES-VVPG-1010:PLC_OPEN", add_prefix=("",), name="esvvpg1010", kind=Kind.omitted, - doc="Endstation valve ES-VVPG-1010", + doc="ES-VVPG-1010", auto_monitor=True, labels={"valve"}, ) -class Shutters(EPSSubDevices): - """Shutters at the cSAXS beamline.""" +class ShuttersFrontend(EPSSubDevices): + """Shutters frontend.""" fe_psh1 = Cpt( EpicsSignalRO, @@ -209,7 +217,7 @@ class Shutters(EPSSubDevices): add_prefix=("",), name="fepsh1", kind=Kind.omitted, - doc="Frontend shutter FE-PSH1-EMLS-0010", + doc="FE-PSH1-EMLS-0010", auto_monitor=True, labels={"shutter"}, ) @@ -219,18 +227,22 @@ class Shutters(EPSSubDevices): add_prefix=("",), name="festo1", kind=Kind.omitted, - doc="Frontend shutter FE-STO1-EMLS-0010", + doc="FE-STO1-EMLS-0010", auto_monitor=True, labels={"shutter"}, ) + +class ShuttersEndstation(EPSSubDevices): + """Shutters at the endstation.""" + es_psh17010 = Cpt( EpicsSignalRO, read_pv="X12SA-OP-PSH1-EMLS-7010:OPEN", add_prefix=("",), name="espsh17010", kind=Kind.omitted, - doc="Endstation shutter OP-PSH1-EMLS-7010", + doc="OP-PSH1-EMLS-7010", auto_monitor=True, labels={"shutter"}, ) @@ -358,7 +370,7 @@ class DMMMonochromator(EPSSubDevices): add_prefix=("",), name="dmm_energy", kind=Kind.omitted, - doc="DMM monochromator energy", + doc="DMM Energy", auto_monitor=True, labels={"energy"}, ) @@ -445,7 +457,7 @@ class CCMMonochromator(EPSSubDevices): add_prefix=("",), name="ccm_energy", kind=Kind.omitted, - doc="CCM monochromator energy", + doc="CCM Energy", auto_monitor=True, labels={"energy"}, ) @@ -569,12 +581,15 @@ class CoolingWater(EPSSubDevices): class EPS(PSIDeviceBase): - alarms = Cpt(EPSAlarms, name="alarms") - valves = Cpt(Valves, name="valves") - shutters = Cpt(Shutters, name="shutters") - dmm_monochromator = Cpt(DMMMonochromator, name="dmm_monochromator") - ccm_monochromator = Cpt(CCMMonochromator, name="ccm_monochromator") - cooling_water = Cpt(CoolingWater, name="cooling_water") + alarms = Cpt(EPSAlarms, name="alarms", doc="EPS Alarms") + valves_frontend = Cpt(ValvesFrontend, name="valves_frontend", doc="Valves Frontend") + valves_optics = Cpt(ValvesOptics, name="valves_optics", doc="Valves Optics Hutch") + valves_es = Cpt(ValvesEndstation, name="valves_es", doc="Valves ES Hutch") + shutters_frontend = Cpt(ShuttersFrontend, name="shutters_frontend", doc="Shutters Frontend") + shutters_es = Cpt(ShuttersEndstation, name="shutters_es", doc="Shutters Endstation") + dmm_monochromator = Cpt(DMMMonochromator, name="dmm_monochromator", doc="DMM Monochromator") + ccm_monochromator = Cpt(CCMMonochromator, name="ccm_monochromator", doc="CCM Monochromator") + cooling_water = Cpt(CoolingWater, name="cooling_water", doc="Cooling Water") # Acknowledgment signals for PLC communication (if needed for future use) ackerr = Cpt( @@ -831,23 +846,26 @@ class EPS(PSIDeviceBase): # ------------------- PRINT START --------------------- print(f"{bold}X12SA EPS status{white}") - for sub_device in self.walk_components(): - print(f"\n{bold}{sub_device.name}{white}") - + for name, component in self._sig_attrs.items(): + sub_device = getattr(self, name) rows = [] - for walk in sub_device.walk_components(): - cpt: Cpt = walk.ancestors[-1] - it: EpicsSignalRO = walk.item + # Only print sub-devices, not individual request signals + if not isinstance(sub_device, Device): + continue + print(f"\n{bold}{component.doc}{white}") + for sub_walk in sub_device.walk_components(): + cpt: Cpt = sub_walk.item + it: EpicsSignalRO = getattr(sub_device, cpt.attr) val = self.safe_get(it) - rows.append((cpt.doc, val, it._read_pv.pvname, it._ophyd_labels_[0], it.attr_name)) + rows.append((cpt.doc, val, it)) - label_width = max(32, *(len(label) for (label, _, _, _, _) in rows)) + label_width = max(32, *(len(label) for (label, _, _) in rows)) - for label, value, pv, kind, _attr in rows: + for label, value, it in rows: fv = fmt_value(value, it) # <-- pass attr to formatter print(f" - {label:<{label_width}} {fv}") - if sub_device.name == "cooling_water": + if sub_device.attr_name == "cooling_water": v1 = self.safe_get(self.cooling_water.op_cs_ecvw_0010) v2 = self.safe_get(self.cooling_water.op_cs_ecvw_0020) diff --git a/tests/tests_devices/test_eps.py b/tests/tests_devices/test_eps.py index 86a590e..61c914f 100644 --- a/tests/tests_devices/test_eps.py +++ b/tests/tests_devices/test_eps.py @@ -1,3 +1,4 @@ +# pylint: skip-file from __future__ import annotations import pytest @@ -78,7 +79,21 @@ def eps(): def test_eps_has_signals(eps): + """Test that all expected PVs are present in the eps device.""" found_pvs = [walk.item._read_pv.pvname for walk in eps.walk_signals()] assert set(found_pvs) == set( ALL_PVS ), f"Expected PVs {ALL_PVS} but found {set(ALL_PVS) - set(found_pvs)}" + + +# pylint: disable=line-too-long +expected_show_all_output = "\x1b[1mX12SA EPS status\x1b[0m\n\n\x1b[1mEPS Alarms\x1b[0m\n - X12SA EPS Alarm count 0\n - FrontEnd MIS Alarm count 0\n\n\x1b[1mValves Frontend\x1b[0m\n - FE-VVPG-0000 \x1b[91mCLOSED\x1b[0m\n - FE-VVPG-1010 \x1b[91mCLOSED\x1b[0m\n - FE-VVFV-2010 \x1b[91mCLOSED\x1b[0m\n - FE-VVPG-2010 \x1b[91mCLOSED\x1b[0m\n\n\x1b[1mValves Optics Hutch\x1b[0m\n - OP-VVPG-1010 \x1b[91mCLOSED\x1b[0m\n - OP-VVPG-2010 \x1b[91mCLOSED\x1b[0m\n - OP-VVPG-3010 \x1b[91mCLOSED\x1b[0m\n - OP-VVPG-3020 \x1b[91mCLOSED\x1b[0m\n - OP-VVPG-4010 \x1b[91mCLOSED\x1b[0m\n - OP-VVPG-5010 \x1b[91mCLOSED\x1b[0m\n - OP-VVPG-6010 \x1b[91mCLOSED\x1b[0m\n - OP-VVPG-7010 \x1b[91mCLOSED\x1b[0m\n\n\x1b[1mValves ES Hutch\x1b[0m\n - ES-VVPG-1010 \x1b[91mCLOSED\x1b[0m\n\n\x1b[1mShutters Frontend\x1b[0m\n - FE-PSH1-EMLS-0010 \x1b[91mCLOSED\x1b[0m\n - FE-STO1-EMLS-0010 \x1b[91mCLOSED\x1b[0m\n\n\x1b[1mShutters Endstation\x1b[0m\n - OP-PSH1-EMLS-7010 \x1b[91mCLOSED\x1b[0m\n\n\x1b[1mDMM Monochromator\x1b[0m\n - DMM Temp Surface 1 0.0\n - DMM Temp Surface 2 0.0\n - DMM Temp Shield 1 (disaster) 0.0\n - DMM Temp Shield 2 (disaster) 0.0\n - DMM Translation ThruPos \x1b[91mINACTIVE\x1b[0m\n - DMM Translation InPos \x1b[91mINACTIVE\x1b[0m\n - DMM Bragg ThruPos \x1b[91mINACTIVE\x1b[0m\n - DMM Bragg InPos \x1b[91mINACTIVE\x1b[0m\n - DMM Heater Fault XTAL 1 \x1b[92mOK\x1b[0m\n - DMM Heater Fault XTAL 2 \x1b[92mOK\x1b[0m\n - DMM Heater Fault Support 1 \x1b[92mOK\x1b[0m\n - DMM Energy 0.0000\n - DMM Position out of beam\n - DMM Stripe Stripe 1 W/B4C\n\n\x1b[1mCCM Monochromator\x1b[0m\n - CCM Temp Crystal 0.0\n - CCM Temp Shield (disaster) 0.0\n - CCM Heater Fault 1 \x1b[92mOK\x1b[0m\n - CCM Heater Fault 2 \x1b[92mOK\x1b[0m\n - CCM Heater Fault 3 \x1b[92mOK\x1b[0m\n - CCM Energy 0.0000\n - CCM Position out of beam\n\n\x1b[1mCooling Water\x1b[0m\n - OP-SL1-EFSW-2010 \x1b[91mFAIL\x1b[0m\n - OP-SL2-EFSW-2010 \x1b[91mFAIL\x1b[0m\n - OP-EB1-EFSW-5010 \x1b[91mFAIL\x1b[0m\n - OP-EB1-EFSW-5020 \x1b[91mFAIL\x1b[0m\n - OP-SL3-EFSW-5010 \x1b[91mFAIL\x1b[0m\n - OP-KB-EFSW-6010 \x1b[91mFAIL\x1b[0m\n - OP-PSH1-EFSW-7010 \x1b[91mFAIL\x1b[0m\n - ES-EB2-EFSW-1010 \x1b[91mFAIL\x1b[0m\n - OP-CS-ECVW-0010 \x1b[91mCLOSED\x1b[0m\n - OP-CS-ECVW-0020 \x1b[91mCLOSED\x1b[0m\n\n\x1b[96mHint:\x1b[0m Both water cooling valves are CLOSED.\nYou can open them using: \x1b[1mdev.x12saEPS.water_cooling_op()\x1b[0m\n" + + +def test_eps_show_all(eps, capsys): + """Test that the show_all method outputs the expected status.""" + eps.show_all() + output = capsys.readouterr().out + assert ( + output == expected_show_all_output + ), f"Expected output does not match actual output.\nExpected:\n{expected_show_all_output}\nActual:\n{output}"