From dbad469c4bfb275783e73c81fbf4776b80aac638 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Mon, 13 Nov 2023 17:31:23 -0600 Subject: [PATCH 1/3] ENH #882 MC USB CTR08 support --- apstools/devices/__init__.py | 3 + apstools/devices/measComp_usb_ctr_support.py | 175 +++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 apstools/devices/measComp_usb_ctr_support.py diff --git a/apstools/devices/__init__.py b/apstools/devices/__init__.py index 13404f70c..fb22a640d 100644 --- a/apstools/devices/__init__.py +++ b/apstools/devices/__init__.py @@ -73,6 +73,9 @@ from .measComp_tc32_support import MeasCompTc32 +from .measComp_usb_ctr_support import MeasCompCtr +from .measComp_usb_ctr_support import MeasCompCtrMcs + from .mixin_base import DeviceMixinBase from .motor_mixins import EpicsMotorDialMixin diff --git a/apstools/devices/measComp_usb_ctr_support.py b/apstools/devices/measComp_usb_ctr_support.py new file mode 100644 index 000000000..dfb222a72 --- /dev/null +++ b/apstools/devices/measComp_usb_ctr_support.py @@ -0,0 +1,175 @@ +""" +Measurement Computing USB-CTR 8-Channel Scaler +++++++++++++++++++++++++++++++++++++++++++++++++++ + +Measurement Computing CTR High-Speed Counter/Timer Device + +https://www.farnell.com/datasheets/3795358.pdf + +There is more to this device than just the 8-channel scaler. +Underlying support: https://github.com/epics-modules/measComp + + +The EPICS support provides for an optional scaler, compatible +with the EPICS scaler record. + +*new in apstools release 1.6.18* + +.. rubric:: Public class(es) +.. autosummary:: + + ~MeasCompCtr + ~MeasCompCtrMcs + +.. rubric:: Internal class(es) +.. autosummary:: + + ~MeasCompCtrDeviceCounterChannel + ~MeasCompCtrDevicePulseGenChannel +""" + + +import logging + +logger = logging.getLogger(__name__) + +logger.info(__file__) + +from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV + + +class MeasCompCtrMcs(Device): + """Measurement Computing USB CTR08 Multi-Channel Scaler Controls.""" + + # https://github.com/epics-modules/measComp/blob/master/measCompApp/Db/measCompMCS.template + absolute_timebase_waveform = Component(EpicsSignal, "AbsTimeWF") + acquiring = Component(EpicsSignal, "Acquiring", kind="config") + channel_advance = Component(EpicsSignal, "ChannelAdvance", kind="config") + client_wait = Component(EpicsSignal, "ClientWait", kind="config") + current_channel = Component(EpicsSignalRO, "CurrentChannel", kind="omitted") + do_read_all = Component(EpicsSignal, "DoReadAll", kind="omitted") + dwell = Component(EpicsSignalWithRBV, "Dwell", kind="config") + elapsed_real = Component(EpicsSignalRO, "ElapsedReal") + enable_client_wait = Component(EpicsSignal, "EnableClientWait", kind="config") + erase_all = Component(EpicsSignal, "EraseAll", kind="omitted") + erase_start = Component(EpicsSignal, "EraseStart", kind="omitted") + hardware_acquiring = Component(EpicsSignalRO, "HardwareAcquiring", kind="config") + max_channels = Component(EpicsSignalRO, "MaxChannels", kind="config") + mcs_counter_1_enable = Component(EpicsSignal, "MCSCounter1Enable", kind="config") + mcs_counter_2_enable = Component(EpicsSignal, "MCSCounter2Enable", kind="config") + mcs_counter_3_enable = Component(EpicsSignal, "MCSCounter3Enable", kind="config") + mcs_counter_4_enable = Component(EpicsSignal, "MCSCounter4Enable", kind="config") + mcs_counter_5_enable = Component(EpicsSignal, "MCSCounter5Enable", kind="config") + mcs_counter_6_enable = Component(EpicsSignal, "MCSCounter6Enable", kind="config") + mcs_counter_7_enable = Component(EpicsSignal, "MCSCounter7Enable", kind="config") + mcs_counter_8_enable = Component(EpicsSignal, "MCSCounter8Enable", kind="config") + mcs_counter_enable = Component(EpicsSignal, "MCSCounterEnable", kind="config") + mcs_dio_enable = Component(EpicsSignal, "MCSDIOEnable", kind="config") + model = Component(EpicsSignalRO, "Model", kind="config", string=True) + n_use_all = Component(EpicsSignal, "NuseAll", kind="omitted") + point_0_action = Component(EpicsSignal, "Point0Action", kind="config") + prescale = Component(EpicsSignal, "Prescale", kind="config") + prescale_counter = Component(EpicsSignal, "PrescaleCounter", kind="config") + preset_real = Component(EpicsSignal, "PresetReal", kind="config") + read_all = Component(EpicsSignal, "ReadAll", kind="omitted") + read_all_once = Component(EpicsSignal, "ReadAllOnce", kind="omitted") + set_acquiring = Component(EpicsSignal, "SetAcquiring", kind="omitted") + set_client_wait = Component(EpicsSignal, "SetClientWait", kind="config") + snl_connected = Component(EpicsSignalRO, "SNL_Connected", kind="config") + start_all = Component(EpicsSignal, "StartAll", kind="omitted") + stop_all = Component(EpicsSignal, "StopAll", kind="omitted") + trigger_mode = Component(EpicsSignal, "TrigMode", kind="config", string=True) + + mca1 = Component(EpicsSignalRO, "mca1", labels=["MCA"]) + mca2 = Component(EpicsSignalRO, "mca2", labels=["MCA"]) + mca3 = Component(EpicsSignalRO, "mca3", labels=["MCA"]) + mca4 = Component(EpicsSignalRO, "mca4", labels=["MCA"]) + mca5 = Component(EpicsSignalRO, "mca5", labels=["MCA"]) + mca6 = Component(EpicsSignalRO, "mca6", labels=["MCA"]) + mca7 = Component(EpicsSignalRO, "mca7", labels=["MCA"]) + mca8 = Component(EpicsSignalRO, "mca8", labels=["MCA"]) + + +class MeasCompCtrDeviceCounterChannel(Device): + """Measurement Computing USB CTR08 Pulse Counter channel.""" + + counts = Component(EpicsSignalRO, "Counts") + reset = Component(EpicsSignal, "Reset", kind="omitted") + + +class MeasCompCtrDevicePulseGenChannel(Device): + """Measurement Computing USB CTR08 Pulse Generator channel.""" + + # Do not connect: (calcout) CalcDutyCycle + # Do not connect: (calcout) CalcFrequency + # Do not connect: (calcout) CalcPeriod + # Do not connect: (calcout) CalcWidth + count = Component(EpicsSignal, "Count") + delay = Component(EpicsSignalWithRBV, "Delay") + duty_cycle = Component(EpicsSignalWithRBV, "DutyCycle") + frequency = Component(EpicsSignalWithRBV, "Frequency") + idle_state = Component(EpicsSignal, "IdleState") + period = Component(EpicsSignalWithRBV, "Period") + run = Component(EpicsSignal, "Run") + width = Component(EpicsSignalWithRBV, "Width") + + +class MeasCompCtr(Device): + """Measurement Computing USB CTR08 high-speed counter/timer.""" + + # https://github.com/epics-modules/measComp/blob/master/measCompApp/Db/measCompDevice.template + model_name = Component(EpicsSignalRO, "ModelName", kind="config", string=True) + model_number = Component(EpicsSignalRO, "ModelNumber", kind="config") + firmware_version = Component(EpicsSignalRO, "FirmwareVersion", kind="config", string=True) + unique_id = Component(EpicsSignalRO, "UniqueID", kind="config", string=True) + ul_version = Component(EpicsSignalRO, "ULVersion", kind="config", string=True) + driver_version = Component(EpicsSignalRO, "DriverVersion", kind="config", string=True) + poll_time_ms = Component(EpicsSignalRO, "PollTimeMS", kind="config") + poll_sleep_ms = Component(EpicsSignalRO, "PollSleepMS", kind="config") + last_error_message = Component(EpicsSignalRO, "LastErrorMessage", kind="config", string=True) + + # https://github.com/epics-modules/measComp/blob/master/measCompApp/Db/USBCTR.substitutions + long_in = Component(EpicsSignalRO, "Li") + + binary_in_1 = Component(EpicsSignalRO, "Bi1") + binary_in_2 = Component(EpicsSignalRO, "Bi2") + binary_in_3 = Component(EpicsSignalRO, "Bi3") + binary_in_4 = Component(EpicsSignalRO, "Bi4") + binary_in_5 = Component(EpicsSignalRO, "Bi5") + binary_in_6 = Component(EpicsSignalRO, "Bi6") + binary_in_7 = Component(EpicsSignalRO, "Bi7") + binary_in_8 = Component(EpicsSignalRO, "Bi8") + + long_out = Component(EpicsSignalWithRBV, "Lo") + + binary_out_1 = Component(EpicsSignalWithRBV, "Bo1") + binary_out_2 = Component(EpicsSignalWithRBV, "Bo2") + binary_out_3 = Component(EpicsSignalWithRBV, "Bo3") + binary_out_4 = Component(EpicsSignalWithRBV, "Bo4") + binary_out_5 = Component(EpicsSignalWithRBV, "Bo5") + binary_out_6 = Component(EpicsSignalWithRBV, "Bo6") + binary_out_7 = Component(EpicsSignalWithRBV, "Bo7") + binary_out_8 = Component(EpicsSignalWithRBV, "Bo8") + + binary_direction_1 = Component(EpicsSignal, "Bd1") + binary_direction_2 = Component(EpicsSignal, "Bd2") + binary_direction_3 = Component(EpicsSignal, "Bd3") + binary_direction_4 = Component(EpicsSignal, "Bd4") + binary_direction_5 = Component(EpicsSignal, "Bd5") + binary_direction_6 = Component(EpicsSignal, "Bd6") + binary_direction_7 = Component(EpicsSignal, "Bd7") + binary_direction_8 = Component(EpicsSignal, "Bd8") + + pulse_gen_1 = Component(MeasCompCtrDevicePulseGenChannel, "PulseGen1") + pulse_gen_2 = Component(MeasCompCtrDevicePulseGenChannel, "PulseGen2") + pulse_gen_3 = Component(MeasCompCtrDevicePulseGenChannel, "PulseGen3") + pulse_gen_4 = Component(MeasCompCtrDevicePulseGenChannel, "PulseGen4") + + counter_1 = Component(MeasCompCtrDeviceCounterChannel, "Counter1") + counter_2 = Component(MeasCompCtrDeviceCounterChannel, "Counter2") + counter_3 = Component(MeasCompCtrDeviceCounterChannel, "Counter3") + counter_4 = Component(MeasCompCtrDeviceCounterChannel, "Counter4") + counter_5 = Component(MeasCompCtrDeviceCounterChannel, "Counter5") + counter_6 = Component(MeasCompCtrDeviceCounterChannel, "Counter6") + counter_7 = Component(MeasCompCtrDeviceCounterChannel, "Counter7") + counter_8 = Component(MeasCompCtrDeviceCounterChannel, "Counter8") From 117d91c5505e6fcd87ee9c6a477edb66d52da906 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Mon, 13 Nov 2023 17:31:37 -0600 Subject: [PATCH 2/3] DOC #882 --- CHANGES.rst | 6 ++++-- docs/source/api/_devices.rst | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 436c99503..4a7df7090 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -28,12 +28,14 @@ describe the future plans. 1.6.18 ****** -release expected by 2023-09-30 +release expected by 2023-12-01 New Features ------------ -* Add ophyd device support for DG-645 digital delay/pulse generator. +* Add (ophyd) device support for + * DG-645 digital delay/pulse generator + * Measurement Computing USB CTR08 High-Speed Counter/Timer 1.6.17 ****** diff --git a/docs/source/api/_devices.rst b/docs/source/api/_devices.rst index 4a0a20100..1553cc15c 100644 --- a/docs/source/api/_devices.rst +++ b/docs/source/api/_devices.rst @@ -83,9 +83,14 @@ Detector & Scaler Support .. autosummary:: ~apstools.devices.struck3820.Struck3820 + ~apstools.devices.measComp_usb_ctr_support.MeasCompCtr + ~apstools.devices.measComp_usb_ctr_support.MeasCompCtrMcs ~apstools.devices.scaler_support.use_EPICS_scaler_channels ~apstools.devices.synth_pseudo_voigt.SynPseudoVoigt +.. tip:: The Measurement Computing USB-CTR08 EPICS support + provides a compatible EPICS scaler record. + .. _devices.flyers: Fly Scan Support @@ -197,6 +202,7 @@ Other Support ~apstools.devices.dict_device_support.dict_device_factory ~apstools.devices.dict_device_support.make_dict_device ~apstools.devices.epics_scan_id_signal.EpicsScanIdSignal + ~apstools.devices.measComp_usb_ctr_support.MeasCompCtr ~apstools.devices.kohzu_monochromator.KohzuSeqCtl_Monochromator ~apstools.devices.flyer_motor_scaler.SignalValueStack ~apstools.devices.srs570_preamplifier.SRS570_PreAmplifier @@ -317,6 +323,12 @@ All Submodules :show-inheritance: :inherited-members: +.. automodule:: apstools.devices.measComp_usb_ctr_support + :members: + :private-members: + :show-inheritance: + :inherited-members: + .. automodule:: apstools.devices.mixin_base :members: :private-members: From b4c71330d284a773eed4544f04a99fa3cf8e323f Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Mon, 13 Nov 2023 17:31:45 -0600 Subject: [PATCH 3/3] TST #882 --- apstools/devices/tests/test_mc_ctr.py | 69 +++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 apstools/devices/tests/test_mc_ctr.py diff --git a/apstools/devices/tests/test_mc_ctr.py b/apstools/devices/tests/test_mc_ctr.py new file mode 100644 index 000000000..93aeb1e35 --- /dev/null +++ b/apstools/devices/tests/test_mc_ctr.py @@ -0,0 +1,69 @@ +""" +test the Measurement Computing USB-CTR device support + +Hardware is not available so test the Python (ophyd) structure. +""" + +from ...tests import IOC_GP +from .. import measComp_usb_ctr_support + +PV_PREFIX = f"phony:{IOC_GP}ctr:" + + +def test_MeasCompCtr(): + controller = measComp_usb_ctr_support.MeasCompCtr(PV_PREFIX, name="controller") + assert not controller.connected + + for attr in """ + model_name + model_number + firmware_version + unique_id + ul_version + driver_version + poll_time_ms + poll_sleep_ms + last_error_message + """.split(): + assert attr in controller.configuration_attrs, f"{attr}" + + attrs = """ + long_in long_out + """.split() + for i in range(4): + attrs.append(f"pulse_gen_{i+1}") + for a in "count delay duty_cycle frequency idle_state period run width".split(): + attrs.append(f"pulse_gen_{i+1}.{a}") + for i in range(8): + attrs.append(f"binary_in_{i+1}") + attrs.append(f"binary_out_{i+1}") + attrs.append(f"binary_direction_{i+1}") + attrs.append(f"counter_{i+1}") + attrs.append(f"counter_{i+1}.counts") + for attr in attrs: + assert attr in controller.read_attrs, f"{attr}" + assert len(attrs) == len(controller.read_attrs) + + +def test_MeasCompCtrMcs(): + controller = measComp_usb_ctr_support.MeasCompCtrMcs(PV_PREFIX, name="controller") + assert not controller.connected + + # spot-check some of the config attributes + for attr in """ + acquiring + dwell + max_channels + model + prescale + prescale_counter + trigger_mode + """.split(): + assert attr in controller.configuration_attrs, f"{attr}" + + attrs = [f"mca{i+1}" for i in range(8)] + attrs.append("absolute_timebase_waveform") + attrs.append("elapsed_real") + for attr in attrs: + assert attr in controller.read_attrs, f"{attr}" + assert len(attrs) == len(controller.read_attrs)