Skip to content

Commit ce1bbd5

Browse files
Merge pull request #1092 from Daniel-Buckelew/Shutter-PR
Shutter Pull Request
2 parents 97abe5a + af39e36 commit ce1bbd5

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

src/navigate/config/configuration_database.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,16 @@
152152
}
153153

154154
shutter_device_types = {
155+
"ASI Shutter": ("ASI", "asi"),
155156
"Analog/Digital Device": ("NI", "ni"),
156157
"Virtual Device": ("Synthetic", "synthetic"),
157158
}
158159

159160
shutter_hardware_widgets = {
160161
"type": ["Device Type", "Combobox", "string", shutter_device_types, None],
161162
"channel": ["NI Channel", "Input", "string", None, "Example: PXI6259/port0/line0"],
163+
"port": ["COM Port", "Input", "string", None, "Example: COM3"],
164+
"axis": ["Shutter Axis", "Input", "string", None, "Example: 1"],
162165
"min": ["Minimum Voltage", "Spinbox", "float", None, "Example: 0"],
163166
"max": ["Maximum Voltage", "Spinbox", "float", None, "Example: 5"],
164167
"frame_config": {"ref": "hardware"},

src/navigate/model/devices/APIs/asi/asi_tiger_controller.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,3 +938,32 @@ def move_dichroic(self, dichroic_id, dichroic_position=0):
938938
"""
939939
self.send_filter_wheel_command(f"MOVE {dichroic_id}={dichroic_position}")
940940
self.read_response()
941+
942+
def logic_card_on(self, axis : str):
943+
"""Turn on the logic card
944+
945+
Parameters
946+
----------
947+
axis : str
948+
The axis of the logic card
949+
"""
950+
951+
axis = int(axis) + 32
952+
self.send_command(f'6 M E = {axis}\r')
953+
self.read_response()
954+
self.send_command(f'6 CCA Z=64\r')
955+
self.read_response()
956+
957+
def logic_card_off(self, axis : str):
958+
"""Turn off the logic card
959+
960+
Parameters
961+
----------
962+
axis : str
963+
The axis of the logic card
964+
"""
965+
axis = int(axis) + 32
966+
self.send_command(f'6 M E = {axis}\r')
967+
self.read_response()
968+
self.send_command(f'6 CCA Z=0\r')
969+
self.read_response()
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Copyright (c) 2021-2024 The University of Texas Southwestern Medical Center.
2+
# All rights reserved.
3+
4+
# Redistribution and use in source and binary forms, with or without
5+
# modification, are permitted for academic and research use only (subject to the
6+
# limitations in the disclaimer below) provided that the following conditions are met:
7+
8+
# * Redistributions of source code must retain the above copyright notice,
9+
# this list of conditions and the following disclaimer.
10+
11+
# * Redistributions in binary form must reproduce the above copyright
12+
# notice, this list of conditions and the following disclaimer in the
13+
# documentation and/or other materials provided with the distribution.
14+
15+
# * Neither the name of the copyright holders nor the names of its
16+
# contributors may be used to endorse or promote products derived from this
17+
# software without specific prior written permission.
18+
19+
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
20+
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
21+
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
24+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27+
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
28+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30+
# POSSIBILITY OF SUCH DAMAGE.
31+
#
32+
33+
# Standard Library Imports
34+
import logging
35+
import traceback
36+
from typing import Any, Dict
37+
38+
# Local Imports
39+
from navigate.model.devices.shutter.base import ShutterBase
40+
from navigate.model.devices.device_types import SerialDevice
41+
from navigate.tools.decorators import log_initialization
42+
from navigate.model.devices.APIs.asi.asi_tiger_controller import TigerController
43+
44+
# Logger Setup
45+
p = __name__.split(".")[1]
46+
logger = logging.getLogger(p)
47+
48+
@log_initialization
49+
class ASIShutter(ShutterBase, SerialDevice):
50+
"""ShutterTTL Class
51+
52+
Triggering for shutters delivered from the TigerController.
53+
Each output keeps their last digital state for as long the device is not
54+
powered down.
55+
"""
56+
57+
def __init__(
58+
self,
59+
microscope_name: str,
60+
device_connection: Any,
61+
configuration: Dict[str, Any],
62+
address=None,
63+
) -> None:
64+
"""Initialize the ASIShutter.
65+
66+
Parameters
67+
----------
68+
microscope_name : str
69+
Name of microscope in configuration
70+
device_connection : Any
71+
Hardware device to connect to
72+
configuration : Dict[str, Any]
73+
Global configuration of the microscope
74+
"""
75+
super().__init__(microscope_name, device_connection, configuration)
76+
77+
self.address = address
78+
79+
self.shutter = device_connection
80+
81+
self.axis = configuration["configuration"]["microscopes"][microscope_name]["shutter"]["hardware"]["axis"]
82+
83+
self.port = configuration["configuration"]["microscopes"][microscope_name]["shutter"]["hardware"]["port"]
84+
85+
@classmethod
86+
def connect(cls, port, baudrate=115200, timeout=0.25):
87+
"""Build ASILaser Serial Port connection
88+
89+
Parameters
90+
----------
91+
port : str
92+
Port for communicating with the shutter, e.g., COM1.
93+
baudrate : int
94+
Baud rate for communicating with the shutter, default is 115200.
95+
timeout : float
96+
Timeout for communicating with the shutter, default is 0.25.
97+
98+
Returns
99+
-------
100+
tiger_controller : TigerController
101+
ASI Tiger Controller object.
102+
"""
103+
# wait until ASI device is ready
104+
tiger_controller = TigerController(port, baudrate)
105+
tiger_controller.connect_to_serial()
106+
if not tiger_controller.is_open():
107+
logger.error("ASI shutter connection failed.")
108+
raise Exception("ASI shutter connection failed.")
109+
return tiger_controller
110+
111+
def __del__(self) -> None:
112+
"""Disconnect from the serial port."""
113+
try:
114+
if self.shutter:
115+
self.shutter.disconnect_from_serial()
116+
logger.debug("TigerController disconnected successfully.")
117+
except Exception as e:
118+
logger.exception(f"Error during cleanup: {traceback.format_exc()}")
119+
120+
def open_shutter(self) -> None:
121+
"""Opens the shutter."""
122+
try:
123+
self.shutter.logic_card_on(self.axis)
124+
logger.debug("ASIShutter opened")
125+
except Exception as e:
126+
logger.exception(f"Shutter not open: {traceback.format_exc()}")
127+
128+
def close_shutter(self) -> None:
129+
"""Closes the shutter."""
130+
try:
131+
self.shutter.logic_card_off(self.axis)
132+
logger.debug("ASIShutter closed")
133+
except Exception as e:
134+
logger.exception(f"Shutter did not close: {traceback.format_exc()}")
135+
136+
@property
137+
def state(self) -> bool:
138+
"""Get the state of the shutter."""
139+
return self.shutter.get_axis_position(self.axis)

0 commit comments

Comments
 (0)