From 257378846fdbd34b3a11c5c5aa6ea10ee92be1d6 Mon Sep 17 00:00:00 2001 From: You Yan Date: Sun, 2 Feb 2025 16:38:05 -0800 Subject: [PATCH 1/3] fixed LDI laser engine errors caused by IlluminationController --- software/control/gui_hcs.py | 6 ++-- software/control/serial_peripherals.py | 39 ++++++++++++++++++++------ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/software/control/gui_hcs.py b/software/control/gui_hcs.py index 71ba2304..390dfa1d 100644 --- a/software/control/gui_hcs.py +++ b/software/control/gui_hcs.py @@ -21,7 +21,7 @@ import squid.config import squid.stage.utils import control.microscope -from control.microscope import LightSourceType, IntensityControlMode, ShutterControlMode +from control.microscope import LightSourceType, IntensityControlMode, ShutterControlMode, IlluminationController log = squid.logging.get_logger(__name__) @@ -371,7 +371,7 @@ def loadHardwareObjects(self): if USE_LDI_SERIAL_CONTROL: try: self.ldi = serial_peripherals.LDI() - self.illuminationController = control.microscope.IlluminationController( + self.illuminationController = IlluminationController( self.microcontroller, self.ldi.intensity_mode, self.ldi.shutter_mode, LightSourceType.LDI, self.ldi ) except Exception: @@ -383,7 +383,7 @@ def loadHardwareObjects(self): import control.celesta self.celesta = control.celesta.CELESTA() - self.illuminationController = control.microscope.IlluminationController( + self.illuminationController = IlluminationController( self.microcontroller, IntensityControlMode.Software, ShutterControlMode.TTL, diff --git a/software/control/serial_peripherals.py b/software/control/serial_peripherals.py index f6dadae6..d051a1e0 100644 --- a/software/control/serial_peripherals.py +++ b/software/control/serial_peripherals.py @@ -3,6 +3,8 @@ import time from typing import Tuple, Optional import struct +from control.microscope import LightSourceType, IntensityControlMode, ShutterControlMode +from control._def import * from squid.abc import LightSource import squid.logging @@ -459,22 +461,34 @@ def set_intensity(self, channel, intensity): intensity = "{:.2f}".format(intensity) self.log.debug("set:" + channel + "=" + intensity + "\r") self.serial_connection.write_and_check("set:" + channel + "=" + intensity + "\r", "ok") - self.log.debug("active channel: " + str(self.active_channel)) def get_intensity(self, channel): - return 0 # To be implemented + try: + response = self.serial_connection.write_and_read("set?\r") + pairs = response.replace('SET:', '').split(',') + intensities = {} + for pair in pairs: + channel, value = pair.split('=') + intensities[int(channel)] = int(value) + return intensity[channel] + except: + return 0 def get_intensity_range(self): return [0, 100] - def set_shutter_state(self, channel, state): + def set_shutter_state(self, channel, on): channel = str(channel) - state = str(state) + state = str(on) self.serial_connection.write_and_check("shutter:" + channel + "=" + state + "\r", "ok") def get_shutter_state(self, channel): - self.serial_connection.write_and_check("shutter?\r", "") - return 0 # To be implemented + try: + response = self.serial_connection.write_and_read("shutter?" + channel + "\r") + state = response.split('=')[1] + return 1 if state == 'OPEN' else 0 + except: + return 0 def set_active_channel(self, channel): self.active_channel = channel @@ -486,6 +500,12 @@ def set_active_channel_shutter(self, state): self.log.debug("shutter:" + channel + "=" + state + "\r") self.serial_connection.write_and_check("shutter:" + channel + "=" + state + "\r", "ok") + def shut_down(self): + for ch in list(set(self.channel_mappings.values())): + self.set_intensity(ch, 0) + self.set_shutter_state(ch, False) + self.serial_connection.close() + class LDI_Simulation(LightSource): """Wrapper for communicating with LDI over serial""" @@ -542,9 +562,9 @@ def get_intensity(self, channel): def get_intensity_range(self): return [0, 100] - def set_shutter_state(self, channel, state): + def set_shutter_state(self, channel, on): channel = str(channel) - state = str(state) + state = str(on) def get_shutter_state(self, channel): return 0 @@ -558,6 +578,9 @@ def set_active_channel_shutter(self, state): state = str(state) self.log.debug("shutter:" + channel + "=" + state + "\r") + def shut_down(self): + pass + class SciMicroscopyLEDArray: """Wrapper for communicating with SciMicroscopy over serial""" From d9a7039fd77a0e31efc0d9f4b509fa0b27c346db Mon Sep 17 00:00:00 2001 From: You Yan Date: Sun, 2 Feb 2025 17:15:25 -0800 Subject: [PATCH 2/3] bug fix; remove power range handling from IlluminationController --- software/control/celesta.py | 51 +++++++------------------- software/control/core/core.py | 3 -- software/control/microscope.py | 13 +------ software/control/serial_peripherals.py | 19 +++------- software/squid/abc.py | 10 ----- 5 files changed, 21 insertions(+), 75 deletions(-) diff --git a/software/control/celesta.py b/software/control/celesta.py index 61fe067b..73d2ebe0 100755 --- a/software/control/celesta.py +++ b/software/control/celesta.py @@ -10,6 +10,10 @@ from squid.abc import LightSource from control.microscope import LightSourceType, IntensityControlMode, ShutterControlMode +import squid.logging + +log = squid.logging.get_logger(__name__) + def lumencor_httpcommand(command="GET IP", ip="192.168.201.200"): """ @@ -43,9 +47,9 @@ def __init__(self, **kwds): self.n_lasers = self.get_number_lasers() self.live = True except: - print(traceback.format_exc()) + log.error(traceback.format_exc()) self.live = False - print("Failed to connect to Lumencor Laser at ip:", ip) + log.error("Failed to connect to Lumencor Laser at ip:", ip) if self.live: [self.pmin, self.pmax] = self.get_intensity_range() @@ -89,7 +93,7 @@ def get_color(self, laser_id): """Returns the color of the current laser""" self.message = lumencor_httpcommand(command="GET CHMAP", ip=self.ip) colors = self.message["message"].split(" ")[2:] - print(colors) + log.info(colors) return colors[int(laser_id)] def get_IP(self): @@ -97,9 +101,6 @@ def get_IP(self): return self.message def get_shutter_control_mode(self): - """ - Return True/False the lasers can be controlled with TTL. - """ self.message = lumencor_httpcommand(command="GET TTLENABLE", ip=self.ip) response = self.message["message"] if response[-1] == "1": @@ -108,9 +109,6 @@ def get_shutter_control_mode(self): return ShutterControlMode.Software def set_shutter_control_mode(self, mode): - """ - Turn on/off external TTL control mode. - """ if mode == ShutterControlMode.TTL: ttl_enable = "1" else: @@ -118,18 +116,12 @@ def set_shutter_control_mode(self, mode): self.message = lumencor_httpcommand(command="SET TTLENABLE " + ttl_enable, ip=self.ip) def get_shutter_state(self, laser_id): - """ - Return True/False the laser is on/off. - """ self.message = lumencor_httpcommand(command="GET CH " + str(laser_id), ip=self.ip) response = self.message["message"] self.on = response[-1] == "1" return self.on def get_intensity_range(self): - """ - Return [minimum power, maximum power]. - """ max_int = 1000 # default self.message = lumencor_httpcommand(command="GET MAXINT", ip=self.ip) if self.message["message"][0] == "A": @@ -137,34 +129,25 @@ def get_intensity_range(self): return [0, max_int] def get_intensity(self, laser_id): - """ - Return the current laser power. - """ self.message = lumencor_httpcommand(command="GET CHINT " + str(laser_id), ip=self.ip) - # print(command = 'GET CHINT '+str(laser_id), ip=self.ip) + log.debug(command = 'GET CHINT '+str(laser_id), ip=self.ip) response = self.message["message"] power = float(response.split(" ")[-1]) - return power + intensity = power / self.pmax * 100 + return intensity def set_shutter_state(self, laser_id, on): - """ - Turn the laser on/off. - """ if on: self.message = lumencor_httpcommand(command="SET CH " + str(laser_id) + " 1", ip=self.ip) self.on = True else: self.message = lumencor_httpcommand(command="SET CH " + str(laser_id) + " 0", ip=self.ip) self.on = False - print("Turning On/Off", self.on, self.message) + log.info("Turning On/Off", self.on, self.message) - def set_intensity(self, laser_id, power_in_mw): - """ - power_in_mw - The desired laser power in mW. - """ - print("Setting Power", power_in_mw, self.message) - if power_in_mw > self.pmax: - power_in_mw = self.pmax + def set_intensity(self, laser_id, intensity): + log.info("Setting intensity to ", intensity) + power_in_mw = self.pmax * intensity / 100 self.message = lumencor_httpcommand( command="SET CHINT " + str(laser_id) + " " + str(int(power_in_mw)), ip=self.ip ) @@ -173,18 +156,12 @@ def set_intensity(self, laser_id, power_in_mw): return False def shut_down(self): - """ - Turn the laser off. - """ if self.live: for i in range(self.n_lasers): self.set_intensity(i, 0) self.set_shutter_state(i, False) def get_status(self): - """ - Get the status - """ return self.live diff --git a/software/control/core/core.py b/software/control/core/core.py index 2d3d1580..5db552c5 100644 --- a/software/control/core/core.py +++ b/software/control/core/core.py @@ -522,9 +522,6 @@ def __init__( self.enable_channel_auto_filter_switching = True - if USE_LDI_SERIAL_CONTROL: - self.ldi = self.microscope.ldi - if SUPPORT_SCIMICROSCOPY_LED_ARRAY: # to do: add error handling self.led_array = serial_peripherals.SciMicroscopyLEDArray( diff --git a/software/control/microscope.py b/software/control/microscope.py index 7bfe7313..5f17d041 100644 --- a/software/control/microscope.py +++ b/software/control/microscope.py @@ -213,7 +213,6 @@ def __init__( self.channel_mappings_software = {} self.is_on = {} self.intensity_settings = {} - self.pmin, self.pmax = 0, 1000 self.current_channel = None if self.light_source_type is not None: @@ -224,7 +223,6 @@ def configure_light_source(self): self.set_intensity_control_mode(self.intensity_control_mode) self.set_shutter_control_mode(self.shutter_control_mode) self.channel_mappings_software = self.light_source.channel_mappings - self.get_intensity_range() for ch in self.channel_mappings_software: self.intensity_settings[ch] = self.get_intensity(ch) self.is_on[ch] = self.light_source.get_shutter_state(self.channel_mappings_software[ch]) @@ -249,14 +247,9 @@ def get_shutter_control_mode(self): self.shutter_control_mode = mode return mode - def get_intensity_range(self, channel=None): - if self.intensity_control_mode == IntensityControlMode.Software: - [self.pmin, self.pmax] = self.light_source.get_intensity_range() - def get_intensity(self, channel): if self.intensity_control_mode == IntensityControlMode.Software: - power = self.light_source.get_intensity(self.channel_mappings_software[channel]) - intensity = power / self.pmax * 100 + intensity = self.light_source.get_intensity(self.channel_mappings_software[channel]) self.intensity_settings[channel] = intensity return intensity # 0 - 100 @@ -267,7 +260,6 @@ def turn_on_illumination(self, channel=None): if self.shutter_control_mode == ShutterControlMode.Software: self.light_source.set_shutter_state(self.channel_mappings_software[channel], on=True) elif self.shutter_control_mode == ShutterControlMode.TTL: - print("TTL!!") # self.microcontroller.set_illumination(self.channel_mappings_TTL[channel], self.intensity_settings[channel]) self.microcontroller.turn_on_illumination() @@ -290,8 +282,7 @@ def set_current_channel(self, channel): def set_intensity(self, channel, intensity): if self.intensity_control_mode == IntensityControlMode.Software: if intensity != self.intensity_settings[channel]: - power = intensity / 100 * self.pmax - self.light_source.set_intensity(self.channel_mappings_software[channel], power) + self.light_source.set_intensity(self.channel_mappings_software[channel], intensity) self.intensity_settings[channel] = intensity self.microcontroller.set_illumination(self.channel_mappings_TTL[channel], intensity) diff --git a/software/control/serial_peripherals.py b/software/control/serial_peripherals.py index d051a1e0..7ef5d0be 100644 --- a/software/control/serial_peripherals.py +++ b/software/control/serial_peripherals.py @@ -432,6 +432,7 @@ def __init__(self, SN="00000001"): 735: 730, 750: 730, } + self.active_channel = None def initialize(self): self.serial_connection.write_and_check("run!\r", "ok") @@ -474,13 +475,14 @@ def get_intensity(self, channel): except: return 0 - def get_intensity_range(self): - return [0, 100] - def set_shutter_state(self, channel, on): channel = str(channel) state = str(on) + if self.active_channel is not None and channel != self.active_channel: + self.set_active_channel_shutter(False) self.serial_connection.write_and_check("shutter:" + channel + "=" + state + "\r", "ok") + if on: + self.active_channel = channel def get_shutter_state(self, channel): try: @@ -490,10 +492,6 @@ def get_shutter_state(self, channel): except: return 0 - def set_active_channel(self, channel): - self.active_channel = channel - self.log.debug("[set active channel to " + str(channel) + "]") - def set_active_channel_shutter(self, state): channel = str(self.active_channel) state = str(state) @@ -559,9 +557,6 @@ def set_intensity(self, channel, intensity): def get_intensity(self, channel): return 0 - def get_intensity_range(self): - return [0, 100] - def set_shutter_state(self, channel, on): channel = str(channel) state = str(on) @@ -569,10 +564,6 @@ def set_shutter_state(self, channel, on): def get_shutter_state(self, channel): return 0 - def set_active_channel(self, channel): - self.active_channel = channel - self.log.debug("[set active channel to " + str(channel) + "]") - def set_active_channel_shutter(self, state): channel = str(self.active_channel) state = str(state) diff --git a/software/squid/abc.py b/software/squid/abc.py index f9f25577..0a1e916c 100644 --- a/software/squid/abc.py +++ b/software/squid/abc.py @@ -106,16 +106,6 @@ def get_intensity(self, channel) -> float: """ pass - @abstractmethod - def get_intensity_range(self) -> Tuple[float, float]: - """ - Get the valid intensity range. - - Returns: - Tuple[float, float]: (minimum intensity, maximum intensity) - """ - pass - @abstractmethod def shut_down(self): """Safely shut down the light source.""" From 411f9245efe56bb72ccd71b7788c458103902817 Mon Sep 17 00:00:00 2001 From: You Yan Date: Sun, 2 Feb 2025 17:23:32 -0800 Subject: [PATCH 3/3] use None on unexpected serial response --- software/control/serial_peripherals.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/software/control/serial_peripherals.py b/software/control/serial_peripherals.py index 7ef5d0be..ac480634 100644 --- a/software/control/serial_peripherals.py +++ b/software/control/serial_peripherals.py @@ -473,7 +473,7 @@ def get_intensity(self, channel): intensities[int(channel)] = int(value) return intensity[channel] except: - return 0 + return None def set_shutter_state(self, channel, on): channel = str(channel) @@ -490,7 +490,7 @@ def get_shutter_state(self, channel): state = response.split('=')[1] return 1 if state == 'OPEN' else 0 except: - return 0 + return None def set_active_channel_shutter(self, state): channel = str(self.active_channel)