Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

282-standardise-handling-of-read-config-and-hinted-signals-for-standarddetector #468

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ophyd_async/epics/adcore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
NDAttributeDataType,
NDAttributeParam,
NDAttributePv,
NDAttributePvDataType,
NDAttributePvDbrType,
stop_busy_record,
)

Expand All @@ -36,5 +36,5 @@
"NDAttributeParam",
"NDAttributeDataType",
"stop_busy_record",
"NDAttributePvDataType",
"NDAttributePvDbrType",
]
24 changes: 23 additions & 1 deletion src/ophyd_async/epics/adcore/_core_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,29 @@ def __init__(self, prefix: str, name: str = "") -> None:


class NDPluginStatsIO(NDPluginBaseIO):
pass
"""
Plugin for computing statistics from an image or region of interest within an image.
Each boolean signal enables or disables all signals in the appropriate Enum class.
The enum signals may used in the ScalarSignals kwargs of a HDFWriter, and are also
read-only signals on the plugin.
"""

def __init__(self, prefix: str, name: str = "") -> None:
self.total = epics_signal_rw(float, prefix + "TotalArray")
self.statistics = epics_signal_rw(bool, prefix + "ComputeStatistics")
self.statistics_background_width = epics_signal_rw(int, prefix + "BgdWidth")
self.centroid = epics_signal_rw(bool, prefix + "ComputeCentroid")
self.centroid_threshold = epics_signal_rw(float, prefix + "CentroidThreshold")
self.profiles = epics_signal_rw(bool, prefix + "ComputeProfiles")
self.profile_size_x = epics_signal_rw(int, prefix + "ProfileSizeX")
self.profile_cursor_x = epics_signal_rw(int, prefix + "CursorX")
self.profile_size_y = epics_signal_rw(int, prefix + "ProfileSizeY")
self.profile_cursor_y = epics_signal_rw(int, prefix + "CursorY")
self.histogram = epics_signal_rw(bool, prefix + "ComputeHistogram")
self.histogram_max = epics_signal_rw(float, prefix + "HistMax")
self.histogram_min = epics_signal_rw(float, prefix + "HistMin")
self.histogram_size = epics_signal_rw(int, prefix + "HistSize")
super().__init__(prefix, name)


class DetectorState(str, Enum):
Expand Down
18 changes: 10 additions & 8 deletions src/ophyd_async/epics/adcore/_hdf_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(
self._name_provider = name_provider
self._shape_provider = shape_provider

self._plugins = plugins or []
self._plugins = plugins
self._capture_status: Optional[AsyncStatus] = None
self._datasets: List[HDFDataset] = []
self._file: Optional[HDFFile] = None
Expand Down Expand Up @@ -105,18 +105,20 @@ async def open(self, multiplier: int = 1) -> Dict[str, DataKey]:
root = ET.fromstring(maybe_xml)
for child in root:
datakey = child.attrib["name"]
if child.attrib.get("type", "EPICS_PV") == "EPICS_PV":
np_datatye = convert_pv_dtype_to_np(
child.attrib.get("dbrtype", "DBR_NATIVE")
)
else:
np_datatye = convert_param_dtype_to_np(
child.attrib.get("datatype", "INT")
)
self._datasets.append(
HDFDataset(
datakey,
f"/entry/instrument/NDAttributes/{datakey}",
(),
convert_pv_dtype_to_np(
child.attrib.get("dbrtype", "DBR_NATIVE")
)
if child.attrib.get("type", "EPICS_PV") == "EPICS_PV"
else convert_param_dtype_to_np(
child.attrib.get("datatype", "INT")
),
np_datatye,
multiplier,
)
)
Expand Down
17 changes: 8 additions & 9 deletions src/ophyd_async/epics/adcore/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def convert_ad_dtype_to_np(ad_dtype: ADBaseDataType) -> str:
return ad_dtype_to_np_dtype[ad_dtype]


def convert_pv_dtype_to_np(datatype: str | None) -> str:
def convert_pv_dtype_to_np(datatype: str) -> str:
_pvattribute_to_ad_datatype = {
"DBR_SHORT": ADBaseDataType.Int16,
"DBR_ENUM": ADBaseDataType.Int16,
Expand All @@ -45,9 +45,9 @@ def convert_pv_dtype_to_np(datatype: str | None) -> str:
"DBR_FLOAT": ADBaseDataType.Float32,
"DBR_DOUBLE": ADBaseDataType.Float64,
}
if datatype in ["STRING", "DBR_STRING", "DBR_CHAR"]:
if datatype in ["DBR_STRING", "DBR_CHAR"]:
np_datatype = "s40"
elif datatype == "DBR_NATIVE" or datatype is None:
elif datatype == "DBR_NATIVE":
raise ValueError("Don't support DBR_NATIVE yet")
else:
try:
Expand All @@ -57,15 +57,14 @@ def convert_pv_dtype_to_np(datatype: str | None) -> str:
return np_datatype


def convert_param_dtype_to_np(datatype: str | None) -> str:
def convert_param_dtype_to_np(datatype: str) -> str:
_paramattribute_to_ad_datatype = {
"INT": ADBaseDataType.Float32,
"INT": ADBaseDataType.Int32,
"INT64": ADBaseDataType.Int64,
"DOUBLE": ADBaseDataType.Float64,
}
if datatype in ["STRING"]:
np_datatype = "s40"
elif datatype is None:
return convert_ad_dtype_to_np(_paramattribute_to_ad_datatype["INT"])
else:
try:
np_datatype = convert_ad_dtype_to_np(
Expand Down Expand Up @@ -94,7 +93,7 @@ class NDAttributeDataType(str, Enum):
STRING = "STRING"


class NDAttributePvDataType(str, Enum):
class NDAttributePvDbrType(str, Enum):
DBR_SHORT = "DBR_SHORT"
DBR_ENUM = "DBR_ENUM"
DBR_INT = "DBR_INT"
Expand All @@ -110,7 +109,7 @@ class NDAttributePv:
name: str # name of attribute stamped on array, also scientifically useful name
# when appended to device.name
signal: SignalR # caget the pv given by signal.source and attach to each frame
datatype: NDAttributePvDataType
dbrtype: NDAttributePvDbrType
description: str = "" # A description that appears in the HDF file as an attribute


Expand Down
2 changes: 1 addition & 1 deletion src/ophyd_async/plan_stubs/_nd_attributes.py
ZohebShaikh marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def setup_ndattributes(
name=ndattribute.name,
type="EPICS_PV",
source=ndattribute.signal.source,
datatype=ndattribute.datatype.value,
dbrtype=ndattribute.dbrtype.value,
description=ndattribute.description,
)
else:
Expand Down
51 changes: 46 additions & 5 deletions tests/epics/adcore/test_writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,19 @@ async def test_stats_describe_when_plugin_configured(
hdf_writer_with_stats._plugins[0].nd_attributes_file,
str("""<?xml version='1.0' encoding='utf-8'?>
<Attributes>
<Attribute name="mydetector-sum" type="PARAM" source="TOTAL" addr="0"
datatype="DOUBLE" description="Sum of each detector frame" />
</Attributes>"""),
<Attribute
name="mydetector-sum"
type="PARAM"
source="TOTAL" addr="0"
datatype="DOUBLE"
description="Sum of each detector frame" />
<Attribute
name="mydetector-Temperature"
type="EPICS_PV"
source="ca://LINKAM:TEMP"
ZohebShaikh marked this conversation as resolved.
Show resolved Hide resolved
dbrtype="DBR_FLOAT"/>
</Attributes>
"""),
)
with patch("ophyd_async.core._signal.wait_for_value", return_value=None):
descriptor = await hdf_writer_with_stats.open()
Expand All @@ -107,10 +117,41 @@ async def test_stats_describe_when_plugin_configured(
"dtype_numpy": "<f8",
"external": "STREAM:",
},
"mydetector-Temperature": {
"dtype": "number",
"dtype_numpy": "<f4",
"external": "STREAM:",
"shape": (),
"source": "mock+ca://HDF:FullFileName_RBV",
},
}
await hdf_writer_with_stats.close()


async def test_stats_describe_raises_error_with_dbr_native(
hdf_writer_with_stats: adcore.ADHDFWriter,
):
assert hdf_writer_with_stats._file is None
set_mock_value(hdf_writer_with_stats.hdf.file_path_exists, True)
set_mock_value(
hdf_writer_with_stats._plugins[0].nd_attributes_file,
str("""<?xml version='1.0' encoding='utf-8'?>
<Attributes>
<Attribute
name="mydetector-Temperature"
type="EPICS_PV"
source="ca://LINKAM:TEMP"
dbrtype="DBR_NATIVE"/>
</Attributes>
"""),
)
with pytest.raises(ValueError) as e:
with patch("ophyd_async.core._signal.wait_for_value", return_value=None):
await hdf_writer_with_stats.open()
await hdf_writer_with_stats.close()
assert str(e.value) == "Don't support DBR_NATIVE yet"


async def test_stats_describe_when_plugin_configured_in_memory(RE, detectors):
for detector in detectors:
await detector.connect(mock=True)
Expand Down Expand Up @@ -143,7 +184,7 @@ async def test_nd_attributes_plan_stub(RE, detectors):
name="Temperature",
signal=epics_signal_r(str, "LINKAM:TEMP"),
description="The sample temperature",
datatype=adcore.NDAttributePvDataType.DBR_FLOAT,
dbrtype=adcore.NDAttributePvDbrType.DBR_FLOAT,
)
RE(setup_ndattributes(detector.hdf, [pv, param]))
xml = await detector.hdf.nd_attributes_file.get_value()
Expand All @@ -152,7 +193,7 @@ async def test_nd_attributes_plan_stub(RE, detectors):
"name": "Temperature",
"type": "EPICS_PV",
"source": "ca://LINKAM:TEMP",
ZohebShaikh marked this conversation as resolved.
Show resolved Hide resolved
"datatype": "DBR_FLOAT",
"dbrtype": "DBR_FLOAT",
"description": "The sample temperature",
}

Expand Down
Loading