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

Split out common AD file plugin logic into core writer class, create ADTiffWriter #606

Open
wants to merge 31 commits into
base: main
Choose a base branch
from

Conversation

jwlodek
Copy link
Member

@jwlodek jwlodek commented Oct 8, 2024

This now seems to mostly work, at least from the collection side. The data cannot be read back via tiled at the moment, however, so for now marking as draft, since I may need to edit the docs generated somewhat to account for this. PRs into bluesky and tiled have been merged to allow reading back collected TIFF data with this branch.

Addresses #595 .

TODO:

  • Add tests for tiff writer
  • Make sure we can read the data back through tiled
  • Make sure HDF writer still works as it did.

@jwlodek
Copy link
Member Author

jwlodek commented Oct 8, 2024

@genematx Here are the ophyd async changes that introduce tiff writer support, specifically you can look at collect_stream_docs in the ADWriter class to see what I am emitting

@jwlodek jwlodek marked this pull request as ready for review October 10, 2024 13:19
Copy link
Member

@mrakitin mrakitin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We reviewed the changes interactively with @jwlodek and @RobertSchaffer1, and they make sense to me.

@coretl, if you are happy with the changes, Jakub will rebase the PR branch to resolve the conflicts.

Copy link
Collaborator

@coretl coretl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some ideas that will remove some of the cast statements, what do you reckon?

Comment on lines 51 to 54
@property
def include_file_number(self):
"""Boolean property to toggle AD file number suffix"""
return self._include_file_number
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this property for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically for toggling whether or not we want to include the _%6.6d suffix to the file names. Typically, with TIFF/JPEG, you would always, but with HDF5 it is more case by case - if we want to do a raster scan for example, with one row per HDF file, it would be nice if they could include the number - currently the HDF writer does not

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we do we need it as a read-only property? Isn't it always used internal to that class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it a property to be able to switch the behavior at run time (depending on the experiment type for example). But I think just allowing that to be specified once at init is reasonable as well.

".h5",
"application/x-hdf5",
)
self.hdf = cast(NDFileHDFIO, self.fileio)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid these casts by making the baseclass generic on it:

NDFileIOT = TypeVar("NDFileIOT", bound=NDFileIO)

class ADWriter(DetectorWriter, Generic[NDFileIOT]):
    def __init__(
        self,
        fileio: NDFileIOT,
        ...
    ) -> None:
        self.fileio = fileio  # self.fileio is the same subclass of NDFileIO we passed in
        ...

class ADHDFWriter(ADWriter[NDFileHDFIO]):
    def __init__(
        self,
        fileio: NDFileHDFIO,
        ...
    ) -> None:
        super().__init__(
            fileio,
            ...
         )

    async def open(self, multiplier: int = 1) -> dict[str, DataKey]:
        await asyncio.gather(self.fileio.chunk_size_auto.set(True))  # pyright knows self.fileio is NDFileHDFIO
        ...

Comment on lines 30 to 41
prefix,
path_provider,
adcore.ADHDFWriter,
hdf_suffix,
AravisController,
AravisDriverIO,
drv_suffix=drv_suffix,
name=name,
config_sigs=config_sigs,
gpio_number=gpio_number,
)
self.hdf = self._fileio
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of inserting another class into the heirarchy with many more arguments, could we move out the creation of the driver and writer classes into a utility function instead? I had a go with this idea and came up with:

class AravisDetectorTIFF(StandardDetector[AravisController]):
    def __init__(
        self,
        prefix: str,
        path_provider: PathProvider,
        drv_suffix="cam1:",
        tiff_suffix="TIFF1:",
        gpio_number: AravisController.GPIO_NUMBER = 1,
        config_sigs: Sequence[SignalR] = (),
        name="",
    ):
        self.drv, self.tiff, writer = adcore.areadetector_driver_and_tiff(
            drv_cls=AravisDriverIO,
            prefix=prefix,
            drv_suffix=drv_suffix,
            fileio_suffix=tiff_suffix,
            path_provider=path_provider,
        )
        super().__init__(
            controller=AravisController(self.drv, gpio_number),
            writer=writer,
            config_sigs=config_sigs,
            name=name,
        )

I did some of the changes required so pyright was happy, but didn't make the tests work:
efd86c9

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to have the utility function(s) be factories that take in a drv_cls and writer_cls? It would be nice if we didn't need to make a function for each writer type. I am OK with that approach generally though

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds reasonable, I wonder if we should stretch it even further and say that we make one AravisDetector that has an enum init arg fileio_type that switches between HDF and TIFF?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be ideal I think. I'll work on getting something like that working.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, not sure I love that, but it would be nice to be able to specify the writer and have a default suffix. What if fileio_suffix=None, and

if fileio_suffix is None:
    fileio_suffix = ad_writer_type.value

And have the enum values for the writer types just correspond to the default suffixes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise we use whatever the suffix kwarg provides?

Copy link
Collaborator

@coretl coretl Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found another possible approach which avoids the need to make detector specific subclasses:

ADBaseIOT = TypeVar("ADBaseIOT", bound=ADBaseIO)
NDFileIOT = TypeVar("NDFileIOT", bound=NDFileIO)
NDPluginBaseIOT = TypeVar("NDPluginBaseIOT", bound=NDPluginBaseIO)


class AreaDetector(
    Generic[ADBaseIOT, DetectorControllerT, NDFileIOT],
    StandardDetector[DetectorControllerT],
):
    def __init__(
        self,
        drv: ADBaseIOT,
        controller: DetectorControllerT,
        fileio: NDFileIOT,
        writer: DetectorWriter,
        plugins: dict[str, NDPluginBaseIO],
        config_sigs: Sequence[SignalR] = (),
        name: str = "",
    ):
        self.drv = drv
        self.fileio = fileio
        for name, plugin in plugins.items():
            setattr(self, name, plugin)
        super().__init__(controller, writer, config_sigs, name)

    def get_plugin(
        self, name: str, plugin_type: type[NDPluginBaseIOT] = NDPluginBaseIO
    ) -> NDPluginBaseIOT:
        plugin = getattr(self, name, None)
        if not isinstance(plugin, plugin_type):
            raise TypeError(
                f"Expected {self.name}.{name} to be a {plugin_type}, got {plugin}"
            )
        return plugin

    @classmethod
    def with_hdf(
        cls,
        drv: ADBaseIOT,
        controller: DetectorControllerT,
        hdf_prefix: str,
        path_provider: PathProvider,
        plugins: dict[str, NDPluginBaseIO],
        config_sigs: Sequence[SignalR] = (),
        name: str = "",
    ) -> AreaDetector[ADBaseIOT, DetectorControllerT, NDFileHDFIO]:
        fileio = NDFileHDFIO(hdf_prefix)
        writer = ADHDFWriter(
            fileio,
            path_provider,
            lambda: det.name,
            ADBaseDatasetDescriber(drv),
            *plugins.values(),
        )
        det = AreaDetector(drv, controller, fileio, writer, plugins, config_sigs, name)
        return det

    @classmethod
    def with_tiff(
        cls,
        drv: ADBaseIOT,
        controller: DetectorControllerT,
        tiff_prefix: str,
        path_provider: PathProvider,
        plugins: dict[str, NDPluginBaseIO],
        config_sigs: Sequence[SignalR] = (),
        name: str = "",
    ) -> AreaDetector[ADBaseIOT, DetectorControllerT, NDFileIO]:
        fileio = NDFileIO(tiff_prefix)
        writer = ADTIFFWriter(
            fileio, path_provider, lambda: det.name, ADBaseDatasetDescriber(drv)
        )
        det = AreaDetector(drv, controller, fileio, writer, plugins, config_sigs, name)
        return det

So now det.drv and det.fileio are always present, and we can do det.get_plugin("stats", NDPluginStatsIO) if we created it with AreaDetector(..., plugins={"stats": NDPluginStatsIO(prefix+"STATS")}

Copy link
Collaborator

@coretl coretl Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the detector specific classes turn into helper functions:

def _aravis_drv_controller(
    prefix: str, drv_suffix: str, gpio_number: AravisController.GPIO_NUMBER
) -> tuple[AravisDriverIO, AravisController]:
    drv = AravisDriverIO(prefix + drv_suffix)
    controller = AravisController(drv, gpio_number)
    return drv, controller


def aravis_detector_hdf(
    prefix: str,
    path_provider: PathProvider,
    drv_suffix="cam1:",
    hdf_suffix="HDF1:",
    gpio_number: AravisController.GPIO_NUMBER = 1,
    plugins: dict[str, NDPluginBaseIO] | None = None,
    config_sigs: Sequence[SignalR] = (),
    name="",
) -> adcore.AreaDetector:
    drv, controller = _aravis_drv_controller(prefix, drv_suffix, gpio_number)
    hdf_prefix = prefix + hdf_suffix
    return adcore.AreaDetector.with_hdf(
        drv, controller, hdf_prefix, path_provider, plugins or {}, config_sigs, name
    )


def aravis_detector_tiff(
    prefix: str,
    path_provider: PathProvider,
    drv_suffix="cam1:",
    tiff_suffix="TIFF:",
    gpio_number: AravisController.GPIO_NUMBER = 1,
    plugins: dict[str, NDPluginBaseIO] | None = None,
    config_sigs: Sequence[SignalR] = (),
    name="",
) -> adcore.AreaDetector:
    drv, controller = _aravis_drv_controller(prefix, drv_suffix, gpio_number)
    tiff_prefix = prefix + tiff_suffix
    return adcore.AreaDetector.with_hdf(
        drv, controller, tiff_prefix, path_provider, plugins or {}, config_sigs, name
    )

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed again to jwlodek#2

@jwlodek
Copy link
Member Author

jwlodek commented Nov 22, 2024

Applied the changes, rebased, and tests are now passing, but for some reason I am having issues running with real hardware. I think I will start a fresh environment on Monday and confirm if I can collect both HDF and TIFF data and read it back via tiled.

@coretl
Copy link
Collaborator

coretl commented Nov 26, 2024

I can see why you've done it, but I would still prefer to pass in the writer instance rather than class into the AreaDetector base class. I've made some suggestions in jwlodek#3

@jwlodek
Copy link
Member Author

jwlodek commented Nov 26, 2024

This is now working and tested with real devices:

In [5]: RE(count([webcam_tiff], 10))
========= Emitting Doc =============
name = 'start'
doc = {'uid': '0724c917-75ed-4254-a08d-e697e8dd8e3b', 'time': 1732655205.9976873, 'versions': {'ophyd': '1.9.0', 'bluesky': '1.13'}, 'scan_id': 2, 'plan_type': 'generator', 'plan_name': 'count', 'detectors': ['webcam1-TIFF'], 'num_points': 10, 'num_intervals': 9, 'plan_args': {'detectors': ['<ophyd_async.epics.adsimdetector._sim.SimDetector object at 0x7f71bd562150>'], 'num': 10, 'delay': 0.0}, 'hints': {'dimensions': [(('time',), 'primary')]}}
============ Done ============
========= Emitting Doc =============
name = 'descriptor'
doc = {'configuration': {'webcam1-TIFF': {'data': {'webcam1-TIFF-drv-acquire_period': 0.0, 'webcam1-TIFF-drv-acquire_time': 0.0}, 'timestamps': {'webcam1-TIFF-drv-acquire_period': 1732655171.695547, 'webcam1-TIFF-drv-acquire_time': 1732655171.694511}, 'data_keys': {'webcam1-TIFF-drv-acquire_period': {'dtype': 'number', 'shape': [], 'dtype_numpy': '<f8', 'source': 'ca://DEV:WEBCAM1:cam1:AcquirePeriod_RBV', 'units': '', 'precision': 3, 'limits': {'control': {'low': 0.0, 'high': 0.0}, 'display': {'low': 0.0, 'high': 0.0}}}, 'webcam1-TIFF-drv-acquire_time': {'dtype': 'number', 'shape': [], 'dtype_numpy': '<f8', 'source': 'ca://DEV:WEBCAM1:cam1:AcquireTime_RBV', 'units': '', 'precision': 3, 'limits': {'control': {'low': 0.0, 'high': 0.0}, 'display': {'low': 0.0, 'high': 0.0}}}}}}, 'data_keys': {'webcam1-TIFF': {'source': 'webcam1-TIFF', 'shape': [480, 640], 'dtype': 'array', 'dtype_numpy': '|u1', 'external': 'STREAM:', 'object_name': 'webcam1-TIFF'}}, 'name': 'primary', 'object_keys': {'webcam1-TIFF': ['webcam1-TIFF']}, 'run_start': '0724c917-75ed-4254-a08d-e697e8dd8e3b', 'time': 1732655206.7924867, 'uid': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f', 'hints': {'webcam1-TIFF': {'fields': ['webcam1-TIFF']}}}
============ Done ============
========= Emitting Doc =============
name = 'stream_resource'
doc = {'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'data_key': 'webcam1-TIFF', 'mimetype': 'multipart/related;type=image/tiff', 'uri': 'file://localhost/tmp/bs_tests/', 'parameters': {'chunk_shape': (1, 480, 640), 'template': '8dfbe026-b30d-497f-985b-ab73ee7dae1f_{:06d}.tiff'}, 'run_start': '0724c917-75ed-4254-a08d-e697e8dd8e3b'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/0', 'seq_nums': {'start': 1, 'stop': 2}, 'indices': {'start': 0, 'stop': 1}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': 'cc38707f-b384-40e0-8441-3b317f5d70e2', 'time': 1732655206.796525, 'data': {}, 'timestamps': {}, 'seq_num': 1, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/1', 'seq_nums': {'start': 2, 'stop': 3}, 'indices': {'start': 1, 'stop': 2}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': '4e147ed1-9eb8-421a-b048-4082bfcb6113', 'time': 1732655207.5757759, 'data': {}, 'timestamps': {}, 'seq_num': 2, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/2', 'seq_nums': {'start': 3, 'stop': 4}, 'indices': {'start': 2, 'stop': 3}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': '28fc5b95-22ec-4a5f-adae-fb8f77024913', 'time': 1732655208.3603282, 'data': {}, 'timestamps': {}, 'seq_num': 3, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/3', 'seq_nums': {'start': 4, 'stop': 5}, 'indices': {'start': 3, 'stop': 4}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': 'b5886da8-4bd7-4076-8560-bbcea6599f26', 'time': 1732655209.1413302, 'data': {}, 'timestamps': {}, 'seq_num': 4, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/4', 'seq_nums': {'start': 5, 'stop': 6}, 'indices': {'start': 4, 'stop': 5}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': 'e204debd-5bdf-4269-b116-4e1b72ab3284', 'time': 1732655209.92484, 'data': {}, 'timestamps': {}, 'seq_num': 5, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/5', 'seq_nums': {'start': 6, 'stop': 7}, 'indices': {'start': 5, 'stop': 6}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': 'f677f729-7f9a-4cf7-824a-ebdc9df49920', 'time': 1732655210.7088835, 'data': {}, 'timestamps': {}, 'seq_num': 6, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/6', 'seq_nums': {'start': 7, 'stop': 8}, 'indices': {'start': 6, 'stop': 7}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': 'c3258d06-fbca-48f8-aa32-a0a100a5dbae', 'time': 1732655211.4864063, 'data': {}, 'timestamps': {}, 'seq_num': 7, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/7', 'seq_nums': {'start': 8, 'stop': 9}, 'indices': {'start': 7, 'stop': 8}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': '707b6bd6-bc2e-41ff-96c6-91dd737b923c', 'time': 1732655212.2648413, 'data': {}, 'timestamps': {}, 'seq_num': 8, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/8', 'seq_nums': {'start': 9, 'stop': 10}, 'indices': {'start': 8, 'stop': 9}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': '485bc78e-9710-46ce-b2b4-3cee9bc3756c', 'time': 1732655213.0456653, 'data': {}, 'timestamps': {}, 'seq_num': 9, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stream_datum'
doc = {'stream_resource': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93', 'uid': 'ef9e0fa9-0e7a-4e69-9615-a1413726ac93/9', 'seq_nums': {'start': 10, 'stop': 11}, 'indices': {'start': 9, 'stop': 10}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'event'
doc = {'uid': 'ba91bc4d-07f6-4316-b981-7632c238aa9d', 'time': 1732655213.8267126, 'data': {}, 'timestamps': {}, 'seq_num': 10, 'filled': {}, 'descriptor': 'f855a3cc-c9bd-4b83-b620-669a7cfaa91f'}
============ Done ============
========= Emitting Doc =============
name = 'stop'
doc = {'uid': '34c4fe19-db60-4eca-b340-3bbb259dcd37', 'time': 1732655213.8281024, 'run_start': '0724c917-75ed-4254-a08d-e697e8dd8e3b', 'exit_status': 'success', 'reason': '', 'num_events': {'primary': 10}}
============ Done ============
Out[5]: ('0724c917-75ed-4254-a08d-e697e8dd8e3b',)

@jwlodek
Copy link
Member Author

jwlodek commented Nov 26, 2024

One thing to note - the hanging I mentioned in real hardware tests was actually not caused by any change I made in this PR, but rather because of this line:

self._capture_status = await set_and_wait_for_value(self.hdf.capture, True)
needing the same kwarg that the driver start acquiring command takes. Otherwise it will hang there forever.

Probably should be fixed in main if we will hold off on this PR for a bit longer.

writer, fileio = writer_cls.writer_and_io(
prefix,
path_provider,
lambda: name,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
lambda: name,
lambda: self.name,

Or event better let writer_cls.writer_and_io do this as in my PR

@@ -87,7 +93,7 @@ def get_deadtime(self, exposure: float | None) -> float:
"""For a given exposure, how long should the time between exposures be"""

@abstractmethod
async def prepare(self, trigger_info: TriggerInfo):
async def prepare(self, trigger_info: TriggerInfo) -> Any:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the return value of this always going to be the same for a given DetectorController, or is it going to differ depending on the type of experiment? DetectorController could be generic on the return value of prepare, so we have some type checking?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should ever return anything...
@jwlodek are there examples where it does return something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think so. For some reason vscode built in type checking was reporting that it was returning a coro and such having a type mismatch. I think it should just be None though. I will switch it back

@jwlodek
Copy link
Member Author

jwlodek commented Nov 27, 2024

Confirmed I can read images back w/ this & bluesky main, most recent beta release of tiled:

In [5]: tiled_client.values().last()['primary']['external']['webcam1-TIFF'].metadata
Out[5]: 
{'data_key': 'webcam1-TIFF',
 'mimetype': 'multipart/related;type=image/tiff',
 'parameters': {'chunk_shape': [1, 480, 640],
                'template': 'd289eb79-aa1e-4c1c-95ff-1cdfe25cb8f9_{:06d}.tiff'},
 'run_start': '19f8f041-45b9-48a0-9853-d9980677ff4f',
 'uid': '54a45fed-e0c2-4f19-86cd-9ed7436a6ad2',
 'uri': 'file://localhost/tmp/bs_tests/'}

In [6]: tiled_client.values().last()['primary']['external']['webcam1-TIFF']
Out[6]: <ArrayClient shape=(10, 480, 640) chunks=((1, 1, 1, 1, 1, 1, 1, 1, 1, 1), (480,), (640,)) dtype=uint8>

In [7]: tiled_client.values().last()['primary']['external']['webcam1-TIFF'][5]
Out[7]: 
array([[ 67,  67,  68, ...,  44,  45,  45],
       [ 66,  67,  67, ...,  44,  44,  44],
       [ 66,  66,  66, ...,  42,  43,  43],
       ...,
       [165, 165, 165, ...,  68,  69,  62],
       [165, 165, 165, ...,  68,  66,  68],
       [165, 165, 165, ...,  71,  66,  75]], dtype=uint8)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants