Skip to content

Commit

Permalink
add support for SCI Microscopy LED array/dome
Browse files Browse the repository at this point in the history
  • Loading branch information
hongquanli committed Apr 10, 2024
1 parent 55788ab commit 2715ab8
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 13 deletions.
7 changes: 5 additions & 2 deletions software/control/_def.py
Expand Up @@ -498,8 +498,11 @@ class SOFTWARE_POS_LIMIT:

CONTROLLER_SN = None

SUPPORT_LED_DOME = False
LED_DOME_SN = None
SUPPORT_SCIMICROSCOPY_LED_ARRAY = False
SCIMICROSCOPY_LED_ARRAY_SN = None
SCIMICROSCOPY_LED_ARRAY_DISTANCE = 50
SCIMICROSCOPY_LED_ARRAY_DEFAULT_NA = 0.8
SCIMICROSCOPY_LED_ARRAY_DEFAULT_COLOR = (1,1,1)

##########################################################
#### start of loading machine specific configurations ####
Expand Down
43 changes: 40 additions & 3 deletions software/control/core.py
Expand Up @@ -42,6 +42,8 @@

import subprocess

import control.serial_peripherals as serial_peripherals

class ObjectiveStore:
def __init__(self, objectives_dict = OBJECTIVES, default_objective = DEFAULT_OBJECTIVE):
self.objectives_dict = objectives_dict
Expand Down Expand Up @@ -416,18 +418,53 @@ def __init__(self,camera,microcontroller,configurationManager,control_illuminati

self.display_resolution_scaling = DEFAULT_DISPLAY_CROP/100

if SUPPORT_SCIMICROSCOPY_LED_ARRAY:
# to do: add error handling
self.led_array = serial_peripherals.SciMicroscopyLEDArray(SCIMICROSCOPY_LED_ARRAY_SN,SCIMICROSCOPY_LED_ARRAY_DISTANCE)
self.led_array.set_NA(SCIMICROSCOPY_LED_ARRAY_DEFAULT_NA)

# illumination control
def turn_on_illumination(self):
self.microcontroller.turn_on_illumination()
if SUPPORT_SCIMICROSCOPY_LED_ARRAY and 'LED matrix' in self.currentConfiguration.name:
self.led_array.turn_on_illumination()
else:
self.microcontroller.turn_on_illumination()
self.illumination_on = True

def turn_off_illumination(self):
self.microcontroller.turn_off_illumination()
if SUPPORT_SCIMICROSCOPY_LED_ARRAY and 'LED matrix' in self.currentConfiguration.name:
self.led_array.turn_off_illumination()
else:
self.microcontroller.turn_off_illumination()
self.illumination_on = False

def set_illumination(self,illumination_source,intensity):
if illumination_source < 10: # LED matrix
self.microcontroller.set_illumination_led_matrix(illumination_source,r=(intensity/100)*LED_MATRIX_R_FACTOR,g=(intensity/100)*LED_MATRIX_G_FACTOR,b=(intensity/100)*LED_MATRIX_B_FACTOR)
if SUPPORT_SCIMICROSCOPY_LED_ARRAY:
# set color
if 'BF LED matrix full_R' in self.currentConfiguration.name:
self.led_array.set_color((1,0,0))
elif 'BF LED matrix full_G' in self.currentConfiguration.name:
self.led_array.set_color((0,1,0))
elif 'BF LED matrix full_B' in self.currentConfiguration.name:
self.led_array.set_color((0,0,1))
else:
self.led_array.set_color(SCIMICROSCOPY_LED_ARRAY_DEFAULT_COLOR)
# set intensity
self.led_array.set_brightness(intensity)
# set mode
if 'BF LED matrix left half' in self.currentConfiguration.name:
self.led_array.set_illumination('dpc.l')
if 'BF LED matrix right half' in self.currentConfiguration.name:
self.led_array.set_illumination('dpc.r')
if 'BF LED matrix top half' in self.currentConfiguration.name:
self.led_array.set_illumination('dpc.t')
if 'BF LED matrix bottom half' in self.currentConfiguration.name:
self.led_array.set_illumination('dpc.b')
if 'BF LED matrix full' in self.currentConfiguration.name:
self.led_array.set_illumination('bf')
else:
self.microcontroller.set_illumination_led_matrix(illumination_source,r=(intensity/100)*LED_MATRIX_R_FACTOR,g=(intensity/100)*LED_MATRIX_G_FACTOR,b=(intensity/100)*LED_MATRIX_B_FACTOR)
else:
self.microcontroller.set_illumination(illumination_source,intensity)

Expand Down
90 changes: 82 additions & 8 deletions software/control/serial_peripherals.py
Expand Up @@ -73,25 +73,39 @@ def open_ser(self, SN=None, VID=None, PID=None, baudrate =None, read_timeout=Non
self.serial = serial.Serial(self.port,**kwargs)


def write_and_check(self, command, expected_response, max_attempts=3, attempt_delay=1, check_prefix=True):
def write_and_check(self, command, expected_response, read_delay=0.1, max_attempts=3, attempt_delay=1, check_prefix=True, print_response=False):
# Write a command and check the response
for attempt in range(max_attempts):
self.serial.write(command.encode())
time.sleep(0.1) # Wait for the command to be sent
time.sleep(read_delay) # Wait for the command to be sent

response = self.serial.readline().decode().strip()
if print_response:
print(response)

# flush the input buffer
while self.serial.in_waiting:
if print_response:
print(self.serial.readline().decode().strip())
else:
self.serial.readline().decode().strip()

# check response
if response == expected_response:
return response

# check prefix if the full response does not match
if check_prefix:
if response.startswith(expected_response):
return response

else:
time.sleep(attempt_delay) # Wait before retrying

raise RuntimeError("Max attempts reached without receiving expected response.")

def write(self, command):
self.serial.write(command.encode())

def close(self):
# Close the serial connection
self.serial.close()
Expand Down Expand Up @@ -149,7 +163,7 @@ def __init__(self, SN="A106QADU"):
"""
self.serial_connection = SerialDevice(SN=SN,baudrate=9600,
bytesize=serial.EIGHTBITS,stopbits=serial.STOPBITS_ONE,
parity=serial.PARITY_NONE,
parity=serial.PARITY_NONE,
xonxoff=False,rtscts=False,dsrdtr=False)
self.serial_connection.open_ser()

Expand Down Expand Up @@ -231,9 +245,7 @@ class LDI:
"""Wrapper for communicating with LDI over serial"""
def __init__(self, SN="00000001"):
"""
Provide serial number (default is that of the device
cephla already has) for device-finding purposes. Otherwise, all
XLight devices should use the same serial protocol
Provide serial number
"""
self.serial_connection = SerialDevice(SN=SN,baudrate=9600,
bytesize=serial.EIGHTBITS,stopbits=serial.STOPBITS_ONE,
Expand Down Expand Up @@ -267,4 +279,66 @@ def set_active_channel_shutter(self,state):
channel = str(self.active_channel)
state = str(state)
print('shutter:'+channel+'='+state+'\r')
self.serial_connection.write_and_check('shutter:'+channel+'='+state+'\r',"ok")
self.serial_connection.write_and_check('shutter:'+channel+'='+state+'\r',"ok")

class SciMicroscopyLEDArray:
"""Wrapper for communicating with SciMicroscopy over serial"""
def __init__(self, SN, array_distance = 50):
"""
Provide serial number
"""
self.serial_connection = SerialDevice(SN=SN,baudrate=115200,
bytesize=serial.EIGHTBITS,stopbits=serial.STOPBITS_ONE,
parity=serial.PARITY_NONE,
xonxoff=False,rtscts=False,dsrdtr=False)
self.serial_connection.open_ser()
self.check_about()
self.set_distance(array_distance)

self.illumination = None

def write(self,command):
self.serial_connection.write_and_check(command+'\r','',read_delay=0.01,print_response=True)

def check_about(self):
self.serial_connection.write_and_check('about'+'\r','=',read_delay=0.01,print_response=True)

def set_distance(self,array_distance):
# array distance in mm
array_distance = str(int(array_distance))
self.serial_connection.write_and_check('sad.'+array_distance+'\r','Current array distance from sample is '+array_distance+'mm',read_delay=0.01,print_response=False)

def set_NA(self,NA):
NA = str(int(NA*100))
self.serial_connection.write_and_check('na.'+NA+'\r','Current NA is 0.'+NA,read_delay=0.01,print_response=False)

def set_color(self,color):
# (r,g,b), 0-1
r = int(255*color[0])
g = int(255*color[1])
b = int(255*color[2])
self.serial_connection.write_and_check(f'sc.{r}.{g}.{b}\r',f'Current color balance values are {r}.{g}.{b}',read_delay=0.01,print_response=False)

def set_brightness(self, brightness):
# 0 to 100
brightness = str(int(255*(brightness/100.0)))
self.serial_connection.write_and_check(f'sb.{brightness}\r',f'Current brightness value is {brightness}.',read_delay=0.01,print_response=False)

def turn_on_bf(self):
self.serial_connection.write_and_check(f'bf\r','-==-',read_delay=0.01,print_response=False)

def turn_on_dpc(self,quadrant):
self.serial_connection.write_and_check(f'dpc.{quadrant[0]}\r','-==-',read_delay=0.01,print_response=False)

def set_illumination(self,illumination):
self.illumination = illumination

def clear(self):
self.serial_connection.write_and_check('x','-==-',read_delay=0.01,print_response=False)

def turn_on_illumination(self):
if self.illumination is not None:
self.serial_connection.write_and_check(f'{self.illumination}\r','-==-',read_delay=0.01,print_response=False)

def turn_off_illumination(self):
self.clear()

0 comments on commit 2715ab8

Please sign in to comment.