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

Distribution of Processing #481

Draft
wants to merge 84 commits into
base: master
Choose a base branch
from
Draft

Distribution of Processing #481

wants to merge 84 commits into from

Conversation

tfarago
Copy link
Contributor

@tfarago tfarago commented Apr 7, 2022

This will enable us to acquire data on a remote server and push it there via a ZMQ stream over network to processing nodes which can also be on different machines. Live preview will be able to subscribe to a stream of data and there will also be a stream splitter able to create a whole topology of processing nodes. The control will be done via concert, but only control, meaning mere synchronization of devices and data processing. All the rest will be done remotely, most probably via remote addons implemented as Tango servers for easy RPC access to them. More detailed info and diagrams to follow...

Todo

  • Bring back PCO time stamp addon from master (it was it addons.py and that got deleted) into decentralize-rebased-onto-master
  • swap decentralize by decentralize-rebased-onto-master
  • Release last centralized concert version
  • make a diagram depicting current situation (Acquisition sends data to a Processor which sends the result to Addon via a Proxy, so the addon doesn't have to care if the processing happens locally or remotely)
  • update the docs
  • test local/remote acquisitions and consumers
  • update concert-examples
  • add some benchmarks
  • fix all TODOs
  • squash all SQUASH commits
  • Replace non-database Tango server run methods with the normal ones or make it a choice
  • Tango: teardown vs. delete_device, only one, both, which?
  • Decide if we need ConcertAsyncioRunner

@tfarago
Copy link
Contributor Author

tfarago commented Apr 7, 2022

This first commit enables us to have local and remote acquisitions and consumers. It's mostly about interfaces and defines a way how to create acquisitions and addons. It also introduces an explicit _Consumer class which encapsulates the consuming coroutine function which is way more explicit than the weird arbitrary passing of functions before.

@tfarago tfarago force-pushed the decentralize branch 2 times, most recently from b909e95 to ff891b3 Compare April 8, 2022 14:11
@MarcusZuber
Copy link
Member

How about introducing a RemoteCamera, that sends to the Consumers instead of the RemoteAcquisition? Then the Experiments would not be different for local and remote acquisition.
But splitting it like you did looks nicer from the code structure (but most probable I have to create a lot of Experiment classes with all the combinations of remote/local etc. then).

@tfarago
Copy link
Contributor Author

tfarago commented Apr 11, 2022

Why are you doing this to me?! :-) I will try.

@codecov
Copy link

codecov bot commented Jul 1, 2022

Codecov Report

Attention: 642 lines in your changes are missing coverage. Please review.

Comparison is base (75c881f) 87.81% compared to head (2b5740d) 82.01%.
Report is 46 commits behind head on master.

❗ Current head 2b5740d differs from pull request most recent head 4f8ef8e. Consider uploading reports for the commit 4f8ef8e to get more accurate results

Files Patch % Lines
concert/networking/base.py 0.00% 184 Missing ⚠️
concert/experiments/addons/tango.py 0.00% 102 Missing ⚠️
concert/experiments/dummy.py 0.00% 99 Missing ⚠️
concert/experiments/addons/base.py 69.80% 61 Missing ⚠️
concert/devices/cameras/dummy.py 52.47% 48 Missing ⚠️
concert/experiments/base.py 54.87% 37 Missing ⚠️
concert/experiments/addons/local.py 81.28% 35 Missing ⚠️
concert/devices/cameras/base.py 67.60% 23 Missing ⚠️
concert/_ipython_setup.py 0.00% 21 Missing ⚠️
concert/session/utils.py 34.61% 17 Missing ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #481      +/-   ##
==========================================
- Coverage   87.81%   82.01%   -5.80%     
==========================================
  Files         115      115              
  Lines        7966     8222     +256     
==========================================
- Hits         6995     6743     -252     
- Misses        971     1479     +508     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

)
await self._params[arg].set(kwargs[arg])

@property
Copy link
Member

Choose a reason for hiding this comment

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

Could you make all 'properties' concert Quantities/Parameters? Otherwise this will be confusing (since there is no async access, which is mandatory for the others to use).

'retrieval_padded_width': q.px,
'retrieval_padded_height': q.px,
'projection_margin': q.px,
'x_region': q.px,
Copy link
Member

Choose a reason for hiding this comment

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

Off-topic: Since years I'm thinking about a elegant way to use vectors/lists in Quantities/Properties. At some point somebody tries to do args.x_region[2] = 10*q.px ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't that work like it should automatically?

Copy link
Member

Choose a reason for hiding this comment

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

No, the getter will give you the vector (or whatever), but the setter will not be called, since only the []-operator is called on the result.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure I know what you mean, but this works:

concert > args = [1, 2 ,3] * q.px                                                                                                             
concert > args                                                                                                                                
array([1, 2, 3]) <Unit('pixel')>
concert > args[0] = 10 * q.px                                                                                                                 
concert > args[0]                                                                                                                             
10 <Unit('pixel')>
concert > args                                                                                                                                
array([10,  2,  3]) <Unit('pixel')>

Copy link
Member

Choose a reason for hiding this comment

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

But this not (independent of quantity etc.):

from concert.base import Parameterizable

class MyParameterizable(Parameterizable):
   my_vector = Parameter()

   async def __ainit__(self):
      self._vec = [1, 2, 3]

   async def _get_my_vector(self):
      return self._my_vector

   async def _set_my_vector(self, vec):
      self._vec = vec
  
p = await MyParameterizable()
p.my_vector[1] = 5

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah that, well numpy managed to override []...

Copy link
Member

Choose a reason for hiding this comment

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

I already thought to do this for the QuantityValue...

Copy link
Contributor Author

@tfarago tfarago Jul 4, 2022

Choose a reason for hiding this comment

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

Good, then I leave it to you 😉

@@ -232,26 +233,39 @@ async def __call__(self, producer):


class GeneralBackprojectArgs(object):
Copy link
Member

Choose a reason for hiding this comment

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

Since you already break (slightly) the interface to the reco-args: I always found it a bit artificial that the reco-args and the Reco were splitted (and not the args where properties of the reco-class). What do you think about merging both classes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will keep it in mind, let me finish up the reco and the remote version of it and then I'll see if it will still be reasonable to merge.


def run_server():
TangoBenchmarker.run_server(
args=['name', '-ORBendPoint', 'giop:tcp::1235', '-v4', '-nodb', '-dlist', 'a/b/c'],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Change this everywhere to a proper DB-based start.

@tfarago tfarago force-pushed the decentralize branch 3 times, most recently from 0c99603 to 3da5890 Compare July 8, 2022 12:11
@tfarago tfarago marked this pull request as draft July 12, 2022 06:48
Tango device for the dummy concert camera sending images over a zmq socket.
"""

endpoint = attribute(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@MarcusZuber do you know why Tango doesn't like inheriting attributes? If you run the TangoDummyCamera server you can set the endpoint without any problem, but try reading it and it just crashes.

self._params_initialized = True

def dynamic_getter(self, attr):
# If this were async def Tango would complain about it never being awaited
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is one of the reasons I will not change the format strings + eval in the reco server right now.

@tfarago
Copy link
Contributor Author

tfarago commented Jul 29, 2022

@MarcusZuber in experiments.base and addons package is a draft of how I see things could go, if you are too bored in the next three weeks you can take a look, I will get back to it then.

tfarago added a commit that referenced this pull request Aug 26, 2022
which will be dealt with in PR #481.
@tfarago tfarago mentioned this pull request Aug 26, 2022
tfarago added a commit that referenced this pull request Sep 12, 2022
which will be dealt with in PR #481.
@MarcusZuber
Copy link
Member

This is slightly connected with this and libuca:

So far we have only one grab()-function, which will give in a buffered mode the oldest not read frames and in an unbuffered mode most probable the last recorded frame.
(I'm currently working on a detector that features the possibility to get the most recent frame as well to use a buffer)

Should we introduce a 2nd grab function for "live images" in libuca and concert?

What is related to this PR: It would be nice to stream the live_images to the concert.experiments.addons.Consumer (aka live_view), but the buffered data to writer/online-reco etc.

@tfarago
Copy link
Contributor Author

tfarago commented Aug 23, 2023

I don't see why not having a camera.grab_live method. In the remote implementation one would then probably set up a PUB-SUB socket pair to disseminate the images.

@MarcusZuber
Copy link
Member

We really need the current implementation of the image walker/writer that handles metadata in this also to work.
The metadata can be sent via a pipe, just the re-combination with the frame needs some thinking.

@tfarago
Copy link
Contributor Author

tfarago commented Oct 31, 2023

We really need the current implementation of the image walker/writer that handles metadata in this also to work. The metadata can be sent via a pipe, just the re-combination with the frame needs some thinking.

Can you give an example why? Is it the motor positions?

@MarcusZuber
Copy link
Member

I add a lot of stuff to the metadata, e.g. synchrotron current, motor positions, x-ray tube parameters, timestamps... That was the whole point of introducing the metadata.

@tfarago
Copy link
Contributor Author

tfarago commented Nov 2, 2023

That's not so critical, it will start to be problematic when you really need some synchronization between metadata and a concrete image. What you mentioned here is easy to add, just another stream(s) with the metadata.

@MarcusZuber
Copy link
Member

I thought a bit more about the whole logging/metadata stuff.

I think in the real use cases it would be ideal to attach metadata to a single image when a camera is run in software-trigger-source (e.g. a special stepped scan of which I have many) and to log time-coded metadata otherwise.

So my suggestion would be that we maybe introduce a trigger_with_metadata(dict) function to the camera, that takes care of the synchronization (so ucad would need to take care to combine the stuff correctly) and a log_metadata to the (remote)walker, that basically does what is basically this

def _write_metadata(self, image):
but with a timestamp instead of the frame number.

@tfarago
Copy link
Contributor Author

tfarago commented Nov 22, 2023

I think this breaks encapsulation a little bit and would rather make the writer a bit more clever. I will come up with a concept and get back to this.

@MarcusZuber
Copy link
Member

I thought a bit more about the whole logging/metadata stuff.

I think in the real use cases it would be ideal to attach metadata to a single image when a camera is run in software-trigger-source (e.g. a special stepped scan of which I have many) and to log time-coded metadata otherwise.

So my suggestion would be that we maybe introduce a trigger_with_metadata(dict) function to the camera, that takes care of the synchronization (so ucad would need to take care to combine the stuff correctly) and a log_metadata to the (remote)walker, that basically does what is basically this

def _write_metadata(self, image):

but with a timestamp instead of the frame number.

cf933cb would be a simple implementation of the 'log_metadata'. Open for discussions.

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

Successfully merging this pull request may close these issues.

None yet

3 participants