Skip to content

Commit e1bf310

Browse files
committed
Initial commit of aurox module.
1 parent 8065880 commit e1bf310

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed

microscope/filterwheels/aurox.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8
3+
#
4+
# Copyright 2019 Mick Phillips (mick.phillips@gmail.com)
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
"""Adds support for Aurox devices
20+
21+
Requires package hidapi."""
22+
23+
import hid
24+
import microscope.devices
25+
from enum import Enum
26+
27+
## Clarity constants. These may differ across products, so mangle names.
28+
# USB IDs
29+
_Clarity__VENDORID = 0x1F0A
30+
_Clarity__PRODUCTID = 0x0088
31+
# Base status
32+
_Clarity__SLEEP = 0x7f
33+
_Clarity__RUN = 0x0f
34+
# Door status
35+
_Clarity__DOOROPEN = 0x01
36+
_Clarity__DOORCLOSED = 0x02
37+
# Disk position/status
38+
_Clarity__SLDPOS0 = 0x00 #disk out of beam path, wide field
39+
_Clarity__SLDPOS1 = 0x01 #disk pos 1, low sectioning
40+
_Clarity__SLDPOS2 = 0x02 #disk pos 2, mid sectioning
41+
_Clarity__SLDPOS3 = 0x03 #disk pos 3, high sectioning
42+
_Clarity__SLDERR = 0xff #An error has occurred in setting slide position (end stops not detected)
43+
_Clarity__SLDMID = 0x10 #slide in mid position (was =0x03 for SD62)
44+
# Filter position/status
45+
_Clarity__FLTPOS1 = 0x01 #Filter in position 1
46+
_Clarity__FLTPOS2 = 0x02 #Filter in position 2
47+
_Clarity__FLTPOS3 = 0x03 #Filter in position 3
48+
_Clarity__FLTPOS4 = 0x04 #Filter in position 4
49+
_Clarity__FLTERR = 0xff #An error has been detected in the filter drive (eg filters not present)
50+
_Clarity__FLTMID = 0x10 #Filter in mid position
51+
# Calibration LED state
52+
_Clarity__CALON = 0x01 #CALibration led power on
53+
_Clarity__CALOFF = 0x02 #CALibration led power off
54+
# Error status
55+
_Clarity__CMDERROR = 0xff #Reply to a command that was not understood
56+
# Commands
57+
_Clarity__GETVERSION = 0x00 #Return 3-byte version number byte1.byte2.byte3
58+
# State commands: single command byte immediately followed by any data.
59+
_Clarity__GETONOFF = 0x12 #No data out, returns 1 byte on/off status
60+
_Clarity__GETDOOR = 0x13 #No data out, returns 1 byte shutter status, or SLEEP if device sleeping
61+
_Clarity__GETSLIDE = 0x14 #No data out, returns 1 byte disk-slide status, or SLEEP if device sleeping
62+
_Clarity__GETFILT = 0x15 #No data out, returns 1 byte filter position, or SLEEP if device sleeping
63+
_Clarity__GETCAL = 0x16 #No data out, returns 1 byte CAL led status, or SLEEP if device sleeping
64+
_Clarity__GETSERIAL = 0x19 #No data out, returns 4 byte BCD serial number (little endian)
65+
_Clarity__FULLSTAT = 0x1f #No data, Returns 10 bytes VERSION[3],ONOFF,SHUTTER,SLIDE,FILT,CAL,??,??
66+
# Run state action commands
67+
_Clarity__SETONOFF = 0x21 #1 byte out on/off status, echoes command or SLEEP
68+
_Clarity__SETSLIDE = 0x23 #1 byte out disk position, echoes command or SLEEP
69+
_Clarity__SETFILT = 0x24 #1 byte out filter position, echoes command or SLEEP
70+
_Clarity__SETCAL = 0x25 #1 byte out CAL led status, echoes command or SLEEP
71+
# Service mode commands. Stops disk spinning for alignment.
72+
_Clarity__SETSVCMODE1 = 0xe0 #1 byte for service mode. SLEEP activates service mode. RUN returns to normal mode.
73+
74+
75+
class Clarity(microscope.devices.FilterWheelBase):
76+
_slide_to_sectioning = {__SLDPOS0: 'bypass',
77+
__SLDPOS1: 'low',
78+
__SLDPOS2: 'mid',
79+
__SLDPOS3: 'high',
80+
__SLDMID: 'mid',}
81+
_positions = 4
82+
83+
def __init__(self, *args, **kwargs):
84+
super().__init__(self, *args, **kwargs)
85+
from threading import Lock
86+
self._lock = Lock()
87+
self._hid = None
88+
self.add_setting("sectioning", "enum",
89+
self.get_slide_position,
90+
lambda val: self.set_slide_position(val),
91+
self._slide_to_sectioning)
92+
93+
def _send_command(self, command, param=0, max_length=16, timeout_ms=100):
94+
if not self._hid:
95+
raise Exception("Not connected to device.")
96+
with self._lock:
97+
buffer = [0x00] * max_length
98+
buffer[0] = command
99+
buffer[1] = param
100+
result = self._hid.write(buffer)
101+
response = self._hid.read(max_length, timeout_ms)
102+
if response[0] != command:
103+
return None
104+
else:
105+
return response[1:]
106+
107+
@property
108+
def is_connected(self):
109+
return self._hid is not None
110+
111+
def open(self):
112+
try:
113+
h = hid.device()
114+
h.open(vendor_id=__VENDORID, product_id=__PRODUCTID)
115+
h.set_nonblocking(False)
116+
except:
117+
raise
118+
self._hid = h
119+
120+
def close(self):
121+
if self.is_connected:
122+
self._hid.close()
123+
self._hid = None
124+
125+
def get_id(self):
126+
return self._send_command(__GETSERIAL)
127+
128+
def _on_enable(self):
129+
if not self.is_connected:
130+
self.open()
131+
self._send_command(__SETONOFF, __RUN)
132+
return self._send_command(__GETONOFF) == __RUN
133+
134+
def _on_disable(self):
135+
self._send_command(__SETONOFF, __SLEEP)
136+
137+
def set_calibration(self, state):
138+
if state:
139+
result = self._send_command(__SETCAL, __CALON)
140+
else:
141+
result = self._send_command(__SETCAL, __CALOFF)
142+
return result
143+
144+
def get_slide_position(self):
145+
"""Get the current slide position"""
146+
result = self._slide_to_sectioning.get(self._send_command(__GETSLIDE), None)
147+
if result is None:
148+
raise Exception("Slide position error.")
149+
return result
150+
151+
def set_slide_position(self, position):
152+
"""Set the slide position"""
153+
result = self._send_command(__SETSLIDE, position)
154+
if result is None:
155+
raise Exception("Slide position error.")
156+
return result
157+
158+
def get_slides(self):
159+
return (self._slide_to_sectioning)
160+
161+
def get_status(self):
162+
# Fetch 10 bytes VERSION[3],ONOFF,SHUTTER,SLIDE,FILT,CAL,??,??
163+
result = self._send_command(__FULLSTAT)
164+
status = {}
165+
status['on'] = result[3] == __RUN
166+
slide = result[4]
167+
status['slide'] = (slide, self._slide_to_sectioning.get(slide, None))
168+
status['filter'] = (result[6], self._filters.get(result[6], None))
169+
status['calibration'] == result[7] == __CALON
170+
return status
171+
172+
# Implemented by FilterWheelBase
173+
#def get_filters(self):
174+
# pass
175+
176+
def get_position(self):
177+
"""Return the current filter position"""
178+
result = self._send_command(__GETFILT)
179+
if result == __FLTERR:
180+
raise Exception("Filter position error.")
181+
return result
182+
183+
def set_position(self, pos):
184+
"""Set the filter position"""
185+
result = self._send_command(__SETFILT, pos)
186+
if result is None:
187+
raise Exception("Filter position error.")
188+
return result

0 commit comments

Comments
 (0)