1
1
#!/usr/bin/env python
2
2
# -*- coding: utf-8 -*-
3
3
4
- ## Copyright (C) 2017 David Pinto <[email protected] >
5
- ## Copyright (C) 2016 Mick Phillips <[email protected] >
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/>.
4
+ # Copyright (C) 2017-2020 David Pinto <[email protected] >
5
+ # Copyright (C) 2016-2020 Mick Phillips <[email protected] >
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
19
20
20
"""Classes for control of microscope components.
21
21
55
55
(TRIGGER_AFTER , TRIGGER_BEFORE , TRIGGER_DURATION , TRIGGER_SOFT ) = range (4 )
56
56
57
57
# Mapping of setting data types to descriptors allowed-value description types.
58
- # For python 2 and 3 compatibility, we convert the type into a descriptor string.
59
- # This avoids problems with, say a python 2 client recognising a python 3
60
- # <class 'int'> as a python 2 <type 'int'>.
58
+ # For python 2 and 3 compatibility, we convert the type into a descriptor
59
+ # string. This avoids problems with, say a python 2 client recognising a
60
+ # python 3 <class 'int'> as a python 2 <type 'int'>.
61
61
DTYPES = {'int' : ('int' , tuple ),
62
62
'float' : ('float' , tuple ),
63
63
'bool' : ('bool' , type (None )),
70
70
str : ('str' , int ),
71
71
tuple : ('tuple' , type (None ))}
72
72
73
- # A utility function to call callables or return value of non-callables.
74
- # noinspection PyPep8
75
- _call_if_callable = lambda f : f () if callable (f ) else f
73
+
74
+ def call_if_callable (f ):
75
+ """Call callables, or return value of non-callables."""
76
+ return f () if callable (f ) else f
76
77
77
78
78
79
class _Setting ():
79
80
# TODO: refactor into subclasses to avoid if isinstance .. elif .. else.
80
81
# Settings classes should be private: devices should use a factory method
81
- # rather than instantiate settings directly; most already use add_setting for this.
82
- def __init__ (self , name , dtype , get_func , set_func = None , values = None , readonly = False ):
82
+ # rather than instantiate settings directly; most already use add_setting
83
+ # for this.
84
+ def __init__ (self , name , dtype , get_func , set_func = None , values = None ,
85
+ readonly = False ):
83
86
"""Create a setting.
84
87
85
88
:param name: the setting's name
@@ -102,7 +105,8 @@ def __init__(self, name, dtype, get_func, set_func=None, values=None, readonly=F
102
105
if dtype not in DTYPES :
103
106
raise Exception ('Unsupported dtype.' )
104
107
elif not (isinstance (values , DTYPES [dtype ][1 :]) or callable (values )):
105
- raise Exception ("Invalid values type for %s '%s': expected function or %s" %
108
+ raise Exception ("Invalid values type for %s '%s':"
109
+ "expected function or %s" %
106
110
(dtype , name , DTYPES [dtype ][1 :]))
107
111
self .dtype = DTYPES [dtype ][0 ]
108
112
self ._get = get_func
@@ -211,11 +215,9 @@ def __init__(self, index=None):
211
215
def __del__ (self ):
212
216
self .shutdown ()
213
217
214
-
215
218
def get_is_enabled (self ):
216
219
return self .enabled
217
220
218
-
219
221
def _on_disable (self ):
220
222
"""Do any device-specific work on disable.
221
223
@@ -268,7 +270,8 @@ def make_safe(self):
268
270
"""Put the device into a safe state."""
269
271
pass
270
272
271
- def add_setting (self , name , dtype , get_func , set_func , values , readonly = False ):
273
+ def add_setting (self , name , dtype , get_func , set_func , values ,
274
+ readonly = False ):
272
275
"""Add a setting definition.
273
276
274
277
:param name: the setting's name
@@ -291,7 +294,8 @@ class with getter, setter, etc., and adding Setting instances as
291
294
if dtype not in DTYPES :
292
295
raise Exception ('Unsupported dtype.' )
293
296
elif not (isinstance (values , DTYPES [dtype ][1 :]) or callable (values )):
294
- raise Exception ("Invalid values type for %s '%s': expected function or %s" %
297
+ raise Exception ("Invalid values type for %s '%s':"
298
+ "expected function or %s" %
295
299
(dtype , name , DTYPES [dtype ][1 :]))
296
300
else :
297
301
self ._settings [name ] = _Setting (name , dtype , get_func , set_func ,
@@ -465,7 +469,6 @@ def enable(self):
465
469
_logger .debug ("... enabled." )
466
470
return self .enabled
467
471
468
-
469
472
def disable (self ):
470
473
"""Disable the data capture device.
471
474
@@ -501,13 +504,14 @@ def _send_data(self, client, data, timestamp):
501
504
# this function name as an argument to set_client, but
502
505
# not sure how to subsequently resolve this over Pyro.
503
506
client .receiveData (data , timestamp )
504
- except (Pyro4 .errors .ConnectionClosedError , Pyro4 .errors .CommunicationError ):
507
+ except (Pyro4 .errors .ConnectionClosedError ,
508
+ Pyro4 .errors .CommunicationError ):
505
509
# Client not listening
506
510
_logger .info ("Removing %s from client stack: disconnected." ,
507
511
client ._pyroUri )
508
512
self ._clientStack = list (filter (client .__ne__ , self ._clientStack ))
509
513
self ._liveClients = self ._liveClients .difference ([client ])
510
- except :
514
+ except Exception :
511
515
raise
512
516
513
517
def _dispatch_loop (self ):
@@ -525,12 +529,13 @@ def _dispatch_loop(self):
525
529
err = e
526
530
else :
527
531
try :
528
- self ._send_data (client , self ._process_data (data ), timestamp )
532
+ self ._send_data (client , self ._process_data (data ),
533
+ timestamp )
529
534
except Exception as e :
530
535
err = e
531
536
if err :
532
- # Raising an exception will kill the dispatch loop. We need another
533
- # way to notify the client that there was a problem.
537
+ # Raising an exception will kill the dispatch loop. We need
538
+ # another way to notify the client that there was a problem.
534
539
_logger .error ("in _dispatch_loop:" , exc_info = err )
535
540
self ._dispatch_buffer .task_done ()
536
541
@@ -543,13 +548,13 @@ def _fetch_loop(self):
543
548
data = self ._fetch_data ()
544
549
except Exception as e :
545
550
_logger .error ("in _fetch_loop:" , exc_info = e )
546
- # Raising an exception will kill the fetch loop. We need another
547
- # way to notify the client that there was a problem.
551
+ # Raising an exception will kill the fetch loop. We need
552
+ # another way to notify the client that there was a problem.
548
553
timestamp = time .time ()
549
554
self ._put (e , timestamp )
550
555
data = None
551
556
if data is not None :
552
- # *** TODO*** Add support for timestamp from hardware.
557
+ # TODO Add support for timestamp from hardware.
553
558
timestamp = time .time ()
554
559
self ._put (data , timestamp )
555
560
else :
@@ -602,7 +607,6 @@ def set_client(self, new_client):
602
607
else :
603
608
_logger .info ("Current client is %s." , str (self ._client ))
604
609
605
-
606
610
@keep_acquiring
607
611
def update_settings (self , settings , init = False ):
608
612
"""Update settings, toggling acquisition if necessary."""
@@ -679,6 +683,7 @@ def __init__(self, **kwargs):
679
683
self .get_roi ,
680
684
self .set_roi ,
681
685
None )
686
+
682
687
def _process_data (self , data ):
683
688
"""Apply self._transform to data."""
684
689
flips = (self ._transform [0 ], self ._transform [1 ])
@@ -708,7 +713,8 @@ def set_transform(self, transform):
708
713
if isinstance (transform , str ):
709
714
transform = literal_eval (transform )
710
715
self ._client_transform = transform
711
- lr , ud , rot = (self ._readout_transform [i ] ^ transform [i ] for i in range (3 ))
716
+ lr , ud , rot = (self ._readout_transform [i ] ^ transform [i ]
717
+ for i in range (3 ))
712
718
if self ._readout_transform [2 ] and self ._client_transform [2 ]:
713
719
lr = not lr
714
720
ud = not ud
@@ -758,7 +764,7 @@ def _get_binning(self):
758
764
pass
759
765
760
766
def get_binning (self ):
761
- """Return a tuple of (horizontal, vertical), corrected for transform."""
767
+ """Return a tuple of (horizontal, vertical) corrected for transform."""
762
768
binning = self ._get_binning ()
763
769
if self ._transform [2 ]:
764
770
# 90 degree rotation
@@ -808,9 +814,9 @@ def set_roi(self, roi):
808
814
maxw , maxh = self .get_sensor_shape ()
809
815
binning = self .get_binning ()
810
816
left , top , width , height = roi
811
- if not width : # 0 or None
817
+ if not width : # 0 or None
812
818
width = maxw // binning .h
813
- if not height : # 0 o rNone
819
+ if not height : # 0 o rNone
814
820
height = maxh // binning .v
815
821
if self ._transform [2 ]:
816
822
roi = ROI (left , top , height , width )
@@ -843,6 +849,7 @@ class TriggerType(Enum):
843
849
FALLING_EDGE = 2
844
850
PULSE = 3
845
851
852
+
846
853
class TriggerMode (Enum ):
847
854
ONCE = 1
848
855
BULB = 2
@@ -866,6 +873,7 @@ class TriggerTargetMixIn(metaclass=abc.ABCMeta):
866
873
@property
867
874
def trigger_mode (self ) -> TriggerMode :
868
875
return self ._trigger_mode
876
+
869
877
@property
870
878
def trigger_type (self ) -> TriggerType :
871
879
return self ._trigger_type
@@ -889,11 +897,11 @@ class SerialDeviceMixIn(metaclass=abc.ABCMeta):
889
897
"""
890
898
def __init__ (self , ** kwargs ):
891
899
super ().__init__ (** kwargs )
892
- ## TODO: We should probably construct the connection here but
893
- ## the Serial constructor takes a lot of arguments, and
894
- ## it becomes tricky to separate those from arguments to
895
- ## the constructor of other parent classes.
896
- self .connection = None # serial.Serial (to be constructed by child)
900
+ # TODO: We should probably construct the connection here but
901
+ # the Serial constructor takes a lot of arguments, and
902
+ # it becomes tricky to separate those from arguments to
903
+ # the constructor of other parent classes.
904
+ self .connection = None # serial.Serial (to be constructed by child)
897
905
self ._comms_lock = threading .RLock ()
898
906
899
907
def _readline (self ):
@@ -962,8 +970,8 @@ def __init__(self, **kwargs) -> None:
962
970
"""
963
971
super ().__init__ (** kwargs )
964
972
965
- self ._patterns = None # type: typing.Optional[numpy.ndarray]
966
- self ._pattern_idx = - 1 # type: int
973
+ self ._patterns = None # type: typing.Optional[numpy.ndarray]
974
+ self ._pattern_idx = - 1 # type: int
967
975
968
976
@property
969
977
@abc .abstractmethod
@@ -1008,7 +1016,7 @@ def queue_patterns(self, patterns: numpy.ndarray) -> None:
1008
1016
"""
1009
1017
self ._validate_patterns (patterns )
1010
1018
self ._patterns = patterns
1011
- self ._pattern_idx = - 1 # none is applied yet
1019
+ self ._pattern_idx = - 1 # none is applied yet
1012
1020
1013
1021
def next_pattern (self ) -> None :
1014
1022
"""Apply the next pattern in the queue.
@@ -1018,7 +1026,7 @@ def next_pattern(self) -> None:
1018
1026
if self ._patterns is None :
1019
1027
raise Exception ("no pattern queued to apply" )
1020
1028
self ._pattern_idx += 1
1021
- self .apply_pattern (self ._patterns [self ._pattern_idx ,:])
1029
+ self .apply_pattern (self ._patterns [self ._pattern_idx , :])
1022
1030
1023
1031
def initialize (self ) -> None :
1024
1032
pass
@@ -1087,29 +1095,28 @@ def set_power_mw(self, mw):
1087
1095
1088
1096
1089
1097
class FilterWheelBase (Device , metaclass = abc .ABCMeta ):
1090
- def __init__ (self , filters : typing .Union [typing .Mapping [int , str ], typing . Iterable ] = [],
1091
- positions : int = 0 , ** kwargs ) -> None :
1098
+ def __init__ (self , filters : typing .Union [typing .Mapping [int , str ],
1099
+ typing . Iterable ] = [], positions : int = 0 , ** kwargs ) -> None :
1092
1100
super ().__init__ (** kwargs )
1093
1101
if isinstance (filters , dict ):
1094
1102
self ._filters = filters
1095
1103
else :
1096
- self ._filters = {i :f for (i , f ) in enumerate (filters )}
1104
+ self ._filters = {i : f for (i , f ) in enumerate (filters )}
1097
1105
self ._inv_filters = {val : key for key , val in self ._filters .items ()}
1098
1106
if not hasattr (self , '_positions' ):
1099
- self ._positions = positions # type: int
1107
+ self ._positions = positions # type: int
1100
1108
# The position as an integer.
1101
1109
# Deprecated: clients should call get_position and set_position;
1102
1110
# still exposed as a setting until cockpit uses set_position.
1103
1111
self .add_setting ('position' ,
1104
1112
'int' ,
1105
1113
self .get_position ,
1106
1114
self .set_position ,
1107
- lambda : (0 , self .get_num_positions ()) )
1108
-
1115
+ lambda : (0 , self .get_num_positions ()))
1109
1116
1110
1117
def get_num_positions (self ) -> int :
1111
1118
"""Returns the number of wheel positions."""
1112
- return (max ( self ._positions , len (self ._filters )))
1119
+ return (max (self ._positions , len (self ._filters )))
1113
1120
1114
1121
@abc .abstractmethod
1115
1122
def get_position (self ) -> int :
@@ -1122,7 +1129,7 @@ def set_position(self, position: int) -> None:
1122
1129
pass
1123
1130
1124
1131
def get_filters (self ) -> typing .List [typing .Tuple [int , str ]]:
1125
- return [(k ,v ) for k ,v in self ._filters .items ()]
1132
+ return [(k , v ) for k , v in self ._filters .items ()]
1126
1133
1127
1134
1128
1135
class ControllerDevice (Device , metaclass = abc .ABCMeta ):
0 commit comments