Skip to content

Commit

Permalink
Merge pull request #1 from micro-manager/main
Browse files Browse the repository at this point in the history
Merging new updates from main fork
  • Loading branch information
jacopoabramo authored Oct 10, 2023
2 parents 44e5cfb + 4248193 commit 2eec1b8
Show file tree
Hide file tree
Showing 14 changed files with 61 additions and 46 deletions.
4 changes: 2 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Sphinx==2.1.0
Sphinx
nbsphinx
pygments<3,>=2.4.1
pygments
ipykernel
sphinx_rtd_theme
docstring-inheritance
5 changes: 4 additions & 1 deletion docs/source/apis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,13 @@ Acquisition APIs

Acquisition
==============
.. currentmodule:: pycromanager
.. currentmodule:: pycromanager.acquisition.acquisition_superclass
.. autoclass:: Acquisition
:members:


.. currentmodule:: pycromanager

multi_d_acquisition_events
===========================
.. autofunction:: multi_d_acquisition_events
Expand Down
8 changes: 4 additions & 4 deletions java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.micro-manager.pycro-manager</groupId>
<artifactId>PycroManagerJava</artifactId>
<version>0.44.2</version>
<version>0.44.5</version>
<packaging>jar</packaging>
<name>Pycro-Manager Java</name>
<description>The Java components of Pycro-Manager</description>
Expand Down Expand Up @@ -54,12 +54,12 @@
<dependency>
<groupId>org.micro-manager.acqengj</groupId>
<artifactId>AcqEngJ</artifactId>
<version>0.33.0</version>
<version>0.34.0</version>
</dependency>
<dependency>
<groupId>org.micro-manager.ndviewer</groupId>
<artifactId>NDViewer</artifactId>
<version>0.10.0</version>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>org.micro-manager.ndtiffstorage</groupId>
Expand Down Expand Up @@ -144,4 +144,4 @@
</repository>
</distributionManagement>

</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class ZMQServer extends ZMQSocketWrapper {
//map of objects that exist in some client of the server
protected final ConcurrentHashMap<String, Object> externalObjects_ = new ConcurrentHashMap<String, Object>();

public static final String VERSION = "5.0.0";
public static final String VERSION = "5.1.0";

private static Function<Class, Object> classMapper_;
private static ZMQServer mainServer_;
Expand Down
1 change: 1 addition & 0 deletions pycromanager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
from pycromanager.core import Core
from pycromanager.zmq_bridge.wrappers import JavaObject, JavaClass, PullSocket, PushSocket
from pycromanager.acquisition.acq_eng_py.main.acq_notification import AcqNotification
from ndtiff import Dataset
from ._version import __version__, version_info
2 changes: 1 addition & 1 deletion pycromanager/_version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version_info = (0, 29, 5)
version_info = (0, 29, 9)
__version__ = ".".join(map(str, version_info))
2 changes: 1 addition & 1 deletion pycromanager/acq_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

def _axes_to_key(axes):
""" Turn axes into a hashable key """
return frozenset(axes.items())
return None if axes is None else frozenset(axes.items())

class AcquisitionFuture:

Expand Down
9 changes: 9 additions & 0 deletions pycromanager/acquisition/acq_eng_py/internal/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,18 @@ def submit_event_iterator_inner():
return self.event_generator_executor.submit(submit_event_iterator_inner)


def check_for_default_devices(self, event: AcquisitionEvent):
xy_stage = self.core.get_xy_stage_device()
z_stage = self.core.get_focus_device()
if event.get_z_position() is not None and (z_stage is None or z_stage == ""):
raise Exception("Event requires a z position, but no Core-Focus device is set")
if event.get_x_position() is not None and (xy_stage is None or xy_stage == ""):
raise Exception("Event requires an x position, but no Core-XYStage device is set")

def process_acquisition_event(self, event: AcquisitionEvent) -> Future:
def process_acquisition_event_inner():
try:
self.check_for_default_devices(event)
if event.acquisition_.is_debug_mode():
self.core.logMessage("Processing event: " + event.to_string())
if event.acquisition_.is_debug_mode():
Expand Down
9 changes: 5 additions & 4 deletions pycromanager/acquisition/acq_eng_py/main/acq_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,22 @@ def to_string():
return "image"

def __init__(self, type, id, phase=None):
if type == AcqNotification.Acquisition.to_string():
if type == AcqNotification.Acquisition or type == AcqNotification.Acquisition.to_string():
self.type = AcqNotification.Acquisition
self.id = id
self.phase = phase
elif type == AcqNotification.Image.to_string() and phase == AcqNotification.Image.DATA_SINK_FINISHED:
elif (type == AcqNotification.Image or type == AcqNotification.Image.to_string()) and \
phase == AcqNotification.Image.DATA_SINK_FINISHED:
self.type = AcqNotification.Image
self.id = id
self.phase = phase
elif phase in [AcqNotification.Camera.PRE_SNAP, AcqNotification.Camera.POST_EXPOSURE,
AcqNotification.Camera.PRE_SEQUENCE_STARTED]:
self.type = AcqNotification.Camera
self.id = json.loads(id)
self.id = json.loads(id) if isinstance(id, str) else id # convert from '{'time': 5}' to {'time': 5}
elif phase in [AcqNotification.Hardware.PRE_HARDWARE, AcqNotification.Hardware.POST_HARDWARE]:
self.type = AcqNotification.Hardware
self.id = json.loads(id)
self.id = json.loads(id) if isinstance(id, str) else id # convert from '{'time': 5}' to {'time': 5}
elif phase == AcqNotification.Image.IMAGE_SAVED:
self.type = AcqNotification.Image
self.id = id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def copy(self):
e.stageDeviceNamesToAxisNames_ = self.stageDeviceNamesToAxisNames_.copy()
e.xPosition_ = self.xPosition_
e.yPosition_ = self.yPosition_
e.zPosition_ = self.zPosition_
e.miniumumStartTime_ms_ = self.miniumumStartTime_ms_
e.slmImage_ = self.slmImage_
e.acquireImage_ = self.acquireImage_
Expand Down
14 changes: 13 additions & 1 deletion pycromanager/acquisition/acquisition_superclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def __init__(
----------
directory : str
saving directory for this acquisition. If it is not supplied, the image data will be stored in RAM
(Java backend only)
name : str
Name of the acquisition. This will be used to generate the folder where the data is saved.
image_process_fn : Callable
Expand Down Expand Up @@ -76,7 +77,7 @@ def __init__(
external timing device that synchronizes with other hardware. Accepts either one argument (the current
acquisition event) or two arguments (current event, event_queue)
notification_callback_fn : Callable
(Experimental) function that will be called whenever a notification is received from the acquisition engine. These
function that will be called whenever a notification is received from the acquisition engine. These
include various stages of the control of hardware and the camera and saving of images. Notification
callbacks will execute asynchronously with respect to the acquisition process. The supplied function
should take a single argument, which will be an AcqNotification object. It should execute quickly,
Expand All @@ -91,6 +92,17 @@ def __init__(
the user
debug : bool
whether to print debug messages
show_display : bool
If True, show the image viewer window. If False, show no viewer. (Java backend only)
saving_queue_size : int
The number of images to queue (in memory) while waiting to write to disk. Higher values should
in theory allow sequence acquisitions to go faster, but requires the RAM to hold images while
they are waiting to save (Java backend only)
timeout :
Timeout in ms for connecting to Java side (Java backend only)
port :
Allows overriding the default port for using Java backends on a different port. Use this
after calling start_headless with the same non-default port (Java backend only)
"""
self._debug = debug
self._dataset = None
Expand Down
39 changes: 12 additions & 27 deletions pycromanager/acquisition/java_backend_acquisitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,25 +234,10 @@ def __init__(
show_display: bool=True,
napari_viewer=None,
saving_queue_size: int=20,
timeout: int=2000,
timeout: int=2500,
port: int=DEFAULT_PORT,
debug: int=False
):
"""
Parameters
----------
show_display : bool
If True, show the image viewer window. If False, show no viewer.
saving_queue_size : int
The number of images to queue (in memory) while waiting to write to disk. Higher values should
in theory allow sequence acquisitions to go faster, but requires the RAM to hold images while
they are waiting to save
timeout :
Timeout in ms for connecting to Java side
port :
Allows overriding the default port for using Java backends on a different port. Use this
after calling start_headless with the same non-default port
"""
# Get a dict of all named argument values (or default values when nothing provided)
arg_names = [k for k in signature(JavaBackendAcquisition.__init__).parameters.keys() if k != 'self']
l = locals()
Expand Down Expand Up @@ -286,7 +271,8 @@ def __init__(

try:
self._remote_notification_handler = JavaObject('org.micromanager.remote.RemoteNotificationHandler',
args=[self._acq], port=self._port, new_socket=False)
args=[self._acq], port=self._port, new_socket=False,
timeout=self._timeout)
self._acq_notification_recieving_thread = self._start_receiving_notifications()
self._acq_notification_dispatcher_thread = self._start_notification_dispatcher(notification_callback_fn)
# TODO: can remove this after this feature has been present for a while
Expand Down Expand Up @@ -476,7 +462,7 @@ def _initialize_image_processor(self, **kwargs):

if kwargs['image_process_fn'] is not None:
java_processor = JavaObject(
"org.micromanager.remote.RemoteImageProcessor", port=self._port
"org.micromanager.remote.RemoteImageProcessor", port=self._port, timeout=self._timeout
)
self._acq.add_image_processor(java_processor)
self._processor_thread = self._start_processor(
Expand All @@ -489,29 +475,29 @@ def _initialize_hooks(self, **kwargs):
self._hook_threads = []
if kwargs['event_generation_hook_fn'] is not None:
hook = JavaObject(
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq]
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq], timeout=self._timeout
)
self._hook_threads.append(self._start_hook(hook, kwargs['event_generation_hook_fn'],
self._event_queue, process=False))
self._acq.add_hook(hook, self._acq.EVENT_GENERATION_HOOK)
if kwargs['pre_hardware_hook_fn'] is not None:
hook = JavaObject(
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq]
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq], timeout=self._timeout
)
self._hook_threads.append(self._start_hook(hook,
kwargs['pre_hardware_hook_fn'], self._event_queue,
process=False))
self._acq.add_hook(hook, self._acq.BEFORE_HARDWARE_HOOK)
if kwargs['post_hardware_hook_fn'] is not None:
hook = JavaObject(
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq]
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq], timeout=self._timeout
)
self._hook_threads.append(self._start_hook(hook, kwargs['post_hardware_hook_fn'],
self._event_queue, process=False))
self._acq.add_hook(hook, self._acq.AFTER_HARDWARE_HOOK)
if kwargs['post_camera_hook_fn'] is not None:
hook = JavaObject(
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq],
"org.micromanager.remote.RemoteAcqHook", port=self._port, args=[self._acq], timeout=self._timeout
)
self._hook_threads.append(self._start_hook(hook, kwargs['post_camera_hook_fn'],
self._event_queue, process=False))
Expand All @@ -523,7 +509,7 @@ def _create_remote_acquisition(self, **kwargs):
# create a new socket for it to run on so that it can have blocking calls without interfering with
# the main socket or other internal sockets
new_socket=True,
port=self._port, args=[core], debug=self._debug)
port=self._port, args=[core], debug=self._debug, timeout=self._timeout)
show_viewer = kwargs['show_display'] is True and kwargs['napari_viewer'] is None
self._acq = acq_factory.create_acquisition(kwargs['directory'], kwargs['name'], show_viewer,
kwargs['saving_queue_size'], self._debug,)
Expand Down Expand Up @@ -665,9 +651,8 @@ def __init__(

def _create_remote_acquisition(self, port, **kwargs):
core = ZMQRemoteMMCoreJ(port=self._port, timeout=self._timeout)
acq_factory = JavaObject(
"org.micromanager.remote.RemoteAcquisitionFactory", port=self._port, args=[core]
)
acq_factory = JavaObject("org.micromanager.remote.RemoteAcquisitionFactory",
port=self._port, args=[core], timeout=self._timeout)

show_viewer = kwargs['show_display'] is True and\
kwargs['napari_viewer'] is None and\
Expand Down Expand Up @@ -711,7 +696,7 @@ def __init__(
show_display: bool=True,
image_saved_fn: callable=None,
saving_queue_size: int=20,
timeout: int=1000,
timeout: int=2500,
port: int=DEFAULT_PORT,
debug: bool=False,
):
Expand Down
9 changes: 6 additions & 3 deletions pycromanager/acquisition/python_backend_acquisitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class PythonBackendAcquisition(Acquisition, metaclass=NumpyDocstringInheritanceM

def __init__(
self,
directory: str=None,
name: str='default_acq_name',
image_process_fn: callable=None,
event_generation_hook_fn: callable = None,
Expand All @@ -30,16 +29,20 @@ def __init__(
napari_viewer=None,
image_saved_fn: callable=None,
debug: int=False,
# Specificly so the directory arg can be absorbed and ignored without error,
**kwargs
):
# Get a dict of all named argument values (or default values when nothing provided)
arg_names = [k for k in signature(PythonBackendAcquisition.__init__).parameters.keys() if k != 'self']
l = locals()
named_args = {arg_name: (l[arg_name] if arg_name in l else
dict(signature(PythonBackendAcquisition.__init__).parameters.items())[arg_name].default)
for arg_name in arg_names }
if 'kwargs' in named_args:
if 'directory' in named_args['kwargs'] and named_args['kwargs']['directory'] is not None:
raise Exception('The directory argument is not supported in Python backend acquisitions')
del named_args['kwargs']
super().__init__(**named_args)
if directory is not None:
raise NotImplementedError('Saving to disk is not yet implemented for the python backend. ')
self._dataset = RAMDataStorage()
self._finished = False
self._notifications_finished = False
Expand Down
2 changes: 1 addition & 1 deletion pycromanager/zmq_bridge/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ class _Bridge:

DEFAULT_PORT = 4827
DEFAULT_TIMEOUT = 500
_EXPECTED_ZMQ_SERVER_VERSION = "5.0.0"
_EXPECTED_ZMQ_SERVER_VERSION = "5.1.0"

_bridge_creation_lock = threading.Lock()
_cached_bridges_by_port_and_thread = {}
Expand Down

0 comments on commit 2eec1b8

Please sign in to comment.