3333 + trigger the camera to generate an image
3434 + when the camera returns the image, calibration is complete
3535"""
36- import functools
3736import hid
3837import logging
3938import microscope .devices
40- import typing
39+ from typing import Mapping
4140from enum import IntEnum
4241
4342_logger = logging .getLogger (__name__ )
@@ -116,32 +115,42 @@ def __init__(self, camera=None, **kwargs) -> None:
116115 del kwargs [key ]
117116 super ().__init__ (** kwargs )
118117 from threading import Lock
118+ # A comms lock.
119119 self ._lock = Lock ()
120+ # The hid connection object
120121 self ._hid = None
121- self ._devices = {}
122122 if camera is None :
123123 self ._cam = None
124- _logger .warn ("No camera specified." )
124+ _logger .warning ("No camera specified." )
125125 else :
126126 self ._cam = camera (** cam_kwargs )
127127 self ._cam .pipeline .append (self ._c_process_data )
128- self ._devices ['camera' ] = self ._cam
128+ # Is processing available?
129+ self ._can_process = False
129130 try :
130131 from clarity_process import ClarityProcessor
132+ self ._can_process = True
131133 except :
132- _logger .warn ("Could not import clarity_process module: no processing available." )
134+ _logger .warning ("Could not import clarity_process module: no processing available." )
135+ # Data processor object, created after calibration
133136 self ._processor = None
137+ # Acquisition mode
134138 self ._mode = Mode .raw
139+ # Add device settings
135140 self .add_setting ("sectioning" , "enum" ,
136141 self .get_slide_position ,
137142 lambda val : self .set_slide_position (val ),
138143 self ._slide_to_sectioning )
139144 self .add_setting ("mode" , "enum" ,
140- lambda : self ._mode ,
145+ lambda : self ._mode . name ,
141146 self .set_mode ,
142147 Mode )
143148
144149 def _c_process_data (self , data ):
150+ """A function to insert into the camera's processing pipeline.
151+
152+ Depending on the mode, this function will pass through, perform
153+ calibration, or deconvolve the camera data."""
145154 if self ._mode == Mode .raw :
146155 return data
147156 elif self ._mode == Mode .difference :
@@ -157,11 +166,14 @@ def _c_process_data(self, data):
157166 raise Exception ("Unrecognised mode: %s" , self ._mode )
158167
159168 @property
160- def devices (self ) -> typing .Mapping [str , microscope .devices .Device ]:
161- return self ._devices
169+ def devices (self ) -> Mapping [str , microscope .devices .Device ]:
170+ """Devices property, required by ControllerDevice interface."""
171+ return {'camera' : self ._cam }
162172
163173 def set_mode (self , mode : Mode ) -> None :
164174 """Set the operation mode"""
175+ if mode in [Mode .calibrate , Mode .difference ] and not self ._can_process :
176+ raise Exception ("Processing not available" )
165177 if mode == Mode .calibrate :
166178 self ._set_calibration (True )
167179 else :
@@ -171,7 +183,10 @@ def set_mode(self, mode: Mode) -> None:
171183 def _send_command (self , command , param = 0 , max_length = 16 , timeout_ms = 100 ):
172184 """Send a command to the Clarity and return its response"""
173185 if not self ._hid :
174- self .open ()
186+ try :
187+ self .open ()
188+ except :
189+ raise Exception ("Connection error" )
175190 with self ._lock :
176191 # The device expects a list of 16 integers
177192 buffer = [0x00 ] * max_length # The 0th element must be 0.
@@ -225,17 +240,13 @@ def get_id(self):
225240 return self ._send_command (__GETSERIAL )
226241
227242 def _on_enable (self ):
228- if not self .is_connected :
229- self .open ()
230243 self ._send_command (__SETONOFF , __RUN )
231244 return self ._send_command (__GETONOFF ) == __RUN
232245
233246 def _on_disable (self ):
234247 self ._send_command (__SETONOFF , __SLEEP )
235248
236249 def _set_calibration (self , state ):
237- # TODO: cockpit will need changes for new calibration method and to
238- # remove references to set_calibration, which is now a private method.
239250 if state :
240251 result = self ._send_command (__SETCAL , __CALON )
241252 else :
@@ -262,12 +273,17 @@ def get_slides(self):
262273 return (self ._slide_to_sectioning )
263274
264275 def get_status (self ):
265- # Fetch 10 bytes VERSION[3],ONOFF,SHUTTER,SLIDE,FILT,CAL,??,??
266- result = self ._send_command (__FULLSTAT )
267- if result is None :
268- return
269276 # A status dict to populate and return
270- status = {}
277+ status = dict .fromkeys (['connected' ,'on' ,'door open' ,'slide' ,
278+ 'filter' ,'calibration' ,'busy' , 'mode' ])
279+ status ['mode' ] = self ._mode .name
280+ # Fetch 10 bytes VERSION[3],ONOFF,SHUTTER,SLIDE,FILT,CAL,??,??
281+ try :
282+ result = self ._send_command (__FULLSTAT )
283+ status ['connected' ] = True
284+ except :
285+ status ['connected' ] = False
286+ return status
271287 # A list to track states, any one of which mean the device is busy.
272288 busy = []
273289 # Disk running
0 commit comments