bluesky / event-model

data model for event-based data collection and analysis
https://blueskyproject.io/event-model
BSD 3-Clause "New" or "Revised" License
15 stars 31 forks source link

Add option for RunRouter to fill documents. #116

Closed danielballan closed 5 years ago

danielballan commented 5 years ago

In several instances (various demos and some beamlines) we have worked out contorted code to handle the pattern:

RunEngine ---> (maybe 0MQ...) --> Filler ---> suitcase

where the consumer could be a suitcase or really anyconsumer that requires filled data, like a live image viewer. There is currently no elegant way to do this because Filler is a sink: there is no way to "subscribe" to it.

One possible solution is to add Filler.subscribe, but another, which is better for resource management and also simpler for users, is to put RunRouter in charge of creating and destroying Filler instances. That's what this PR does.

Notice that it provides hooks to:

This was worked out in conversation with @tacaswell.

This needs tests.

danielballan commented 5 years ago

Tests added. And here's an interactive "test" / demo:

In [1]: from bluesky import RunEngine                                                                                                                                                         

In [2]: from bluesky.plans import count                                                                                                                                                       

In [3]: from ophyd.sim import NumpySeqHandler, img                                                                                                                                            

In [4]: from event_model import RunRouter                                                                                                                                                     

In [5]: RE = RunEngine({})                                                                                                                                                                    

In [6]: from suitcase.tiff_stack import Serializer                                                                                                                                            

In [7]: def factory(name, doc): 
   ...:     serializer = Serializer('/tmp/test_run_router_fills') 
   ...:     serializer(name, doc) 
   ...:     return [serializer], [] 
   ...:                                                                                                                                                                                       

In [8]: rr = RunRouter([factory], {'NPY_SEQ': NumpySeqHandler})                                                                                                                               

In [9]: RE.subscribe(rr)                                                                                                                                                                      
Out[9]: 0

In [10]: RE(count([img]))                                                                                                                                                                     
Out[10]: ('9da4b611-c89b-40df-a7a6-ec1e766f2bf4',)

In [11]: ls /tmp/test_run_router_fills/                                                                                                                                                       
9da4b611-c89b-40df-a7a6-ec1e766f2bf4-primary-img.tiff
danielballan commented 5 years ago

I had to rearrange the order that RunRouter and Filler are defined in the module in order for RunRouter to use Filler is a default value for a parameter. This rearrangement makes it hard to see the actual changes in the full diff. Reviewing this specific commit will make it easier to see the changes.

danielballan commented 5 years ago

The coverage complaint is about the moved code, not the new code.

gwbischof commented 5 years ago

What is the mechanism to use RunRouter without filling? The previous RunRouter didn't fill, and the new RunRouter fills by default. Maybe we shouldn't make RunRouter fill by default so it doesn't break existing code?

danielballan commented 5 years ago

Thanks for keeping an eye on backward-compatibility. True, the new RunRouter fills by default if you give it a handler registry. But the default value of handler_registry is {} and the default value of fill_or_fail is False, so the effect is that it still does not do any filling by default.