BioImager  3.9.1
A .NET microscopy imaging library. Supports various microscopes by using imported libraries & GUI automation. Supported libraries include PriorĀ® & ZeissĀ® & all devices supported by Micromanager 2.0 and python-microscope.
Loading...
Searching...
No Matches
clients.py
1#!/usr/bin/env python3
2
3## Copyright (C) 2020 Mick Phillips <mick.phillips@gmail.com>
4##
5## This file is part of Microscope.
6##
7## Microscope is free software: you can redistribute it and/or modify
8## it under the terms of the GNU General Public License as published by
9## the Free Software Foundation, either version 3 of the License, or
10## (at your option) any later version.
11##
12## Microscope is distributed in the hope that it will be useful,
13## but WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15## GNU General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with Microscope. If not, see <http://www.gnu.org/licenses/>.
19
20"""TODO: complete this docstring
21"""
22
23import inspect
24import itertools
25import queue
26import socket
27import threading
28
29import Pyro4
30
31
32# Pyro configuration. Use pickle because it can serialize numpy ndarrays.
33Pyro4.config.SERIALIZERS_ACCEPTED.add("pickle")
34Pyro4.config.SERIALIZER = "pickle"
35
36LISTENERS = {}
37
38
39class Client:
40 """Base Client object that makes methods on proxy available locally."""
41
42 def __init__(self, url):
43 self._url = url
44 self._proxy = None
45 self._connect()
46
47 def _connect(self):
48 """Connect to a proxy and set up self passthrough to proxy methods."""
49 self._proxy = Pyro4.Proxy(self._url)
50 self._proxy._pyroGetMetadata()
51
52 # Derived classes may over-ride some methods. Leave these alone.
53 my_methods = [
54 m[0] for m in inspect.getmembers(self, predicate=inspect.ismethod)
55 ]
56 methods = set(self._proxy._pyroMethods).difference(my_methods)
57 # But in the case of propertyes, we need to inspect the class.
58 my_properties = [
59 m[0]
60 for m in inspect.getmembers(
61 self.__class__, predicate=inspect.isdatadescriptor
62 )
63 ]
64 properties = set(self._proxy._pyroAttrs).difference(my_properties)
65
66 for attr in itertools.chain(methods, properties):
67 setattr(self, attr, getattr(self._proxy, attr))
68
69
71 """A client that can receive and buffer data."""
72
73 def __init__(self, url):
74 super().__init__(url)
75 self._buffer = queue.Queue()
76 # Register self with a listener.
77 if self._url.split("@")[1].split(":")[0] in ["127.0.0.1", "localhost"]:
78 iface = "127.0.0.1"
79 else:
80 # TODO: support multiple interfaces. Could use ifaddr.get_adapters() to
81 # query ip addresses then pick first interface on the same subnet.
82 iface = socket.gethostbyname(socket.gethostname())
83 if iface not in LISTENERS:
84 LISTENERS[iface] = Pyro4.Daemon(host=iface)
85 lthread = threading.Thread(target=LISTENERS[iface].requestLoop)
86 lthread.daemon = True
87 lthread.start()
88 self._client_uri = LISTENERS[iface].register(self)
89
90 def enable(self):
91 """Set the client on the remote and enable it."""
92 self.set_client(self._client_uri)
93 self._proxy.enable()
94
95 @Pyro4.expose
96 @Pyro4.oneway
97 # noinspection PyPep8Naming
98 # Legacy naming convention.
99 def receiveData(self, data, timestamp, *args):
100 del args
101 self._buffer.put((data, timestamp))
102
103 def trigger_and_wait(self):
104 if not hasattr(self, "trigger"):
105 raise Exception("Device has no trigger method.")
106 self.trigger()
107 return self._buffer.get(block=True)