From d2078a034006c4a01839222c47b82c3785179c70 Mon Sep 17 00:00:00 2001 From: Hongquan Li Date: Thu, 15 Feb 2024 07:54:25 -0500 Subject: [PATCH 1/4] add celesta device --- software/control/celesta.py | 180 ++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100755 software/control/celesta.py diff --git a/software/control/celesta.py b/software/control/celesta.py new file mode 100755 index 000000000..71a58a150 --- /dev/null +++ b/software/control/celesta.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +""" +Generic Lumencor laser control via HTTP (ethernet connection). +Bogdan 3/19 +""" +import urllib.request +import traceback +def lumencor_httpcommand(command = 'GET IP',ip = '192.168.201.200'): + """ + Sends commands to the lumencor system via http. + Plese find commands here: + http://lumencor.com/wp-content/uploads/sites/11/2019/01/57-10018.pdf + """ + command_full = r'http://'+ip+'/service/?command='+command.replace(' ','%20') + with urllib.request.urlopen(command_full) as response: + message = eval(response.read()) # the default is conveniently JSON so eval creates dictionary + return message + +class LumencorLaser(object): + """ + This controls a lumencor object (default: Celesta) using HTTP. + Please connect the provided cat5e, RJ45 ethernet cable between the PC and Lumencor system. + """ + def __init__(self, **kwds): + """ + Connect to the Lumencor system via HTTP and check if you get the right response. + """ + self.on = False + self.ip = kwds.get('ip', '192.168.201.200') + self.laser_id = str(kwds.get('laser_id', 0)) + [self.pmin, self.pmax] = 0,1000 + try: + # See if the system returns back the right IP. + self.message = self.getIP() + assert (self.message['message'] == 'A IP '+self.ip) + assert (int(self.laser_id) self.pmax: + power_in_mw = self.pmax + self.message = lumencor_httpcommand(command ='SET CHINT '+self.laser_id+' '+ str(int(power_in_mw)), ip=self.ip) + if self.message['message'][0]=='A': + return True + return False + def shutDown(self): + """ + Turn the laser off. + """ + if self.live: + self.setPower(0) + self.setLaserOnOff(False) + def getStatus(self): + """ + Get the status + """ + return self.live + + +# +# Testing +# +if (__name__ == "__main__"): + import time + obj = LumencorLaser(laser_id=0,ip = '192.168.201.200') + if obj.getStatus(): + print(obj.getPowerRange()) + print(obj.getLaserOnOff()) + obj.setLaserOnOff(True) + obj.setPower(20.0) + time.sleep(0.1) + obj.shutDown() + +# +# The MIT License +# +# Copyright (c) 2013 Zhuang Lab, Harvard University +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# From bf4225dd9b8f9ab6d208a6f4d50a40bd8bf87fb1 Mon Sep 17 00:00:00 2001 From: Hongquan Li Date: Thu, 15 Feb 2024 08:03:47 -0500 Subject: [PATCH 2/4] minor update --- software/control/celesta.py | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/software/control/celesta.py b/software/control/celesta.py index 71a58a150..a45fe8707 100755 --- a/software/control/celesta.py +++ b/software/control/celesta.py @@ -1,10 +1,12 @@ -#!/usr/bin/env python """ Generic Lumencor laser control via HTTP (ethernet connection). Bogdan 3/19 + +revised HL 2/2024 """ import urllib.request import traceback + def lumencor_httpcommand(command = 'GET IP',ip = '192.168.201.200'): """ Sends commands to the lumencor system via http. @@ -16,7 +18,7 @@ def lumencor_httpcommand(command = 'GET IP',ip = '192.168.201.200'): message = eval(response.read()) # the default is conveniently JSON so eval creates dictionary return message -class LumencorLaser(object): +class CELESTA(object): """ This controls a lumencor object (default: Celesta) using HTTP. Please connect the provided cat5e, RJ45 ethernet cable between the PC and Lumencor system. @@ -45,21 +47,25 @@ def __init__(self, **kwds): self.setExtControl(True) if (not self.getLaserOnOff()): self.setLaserOnOff(False) + def getNumberLasers(self): """Return the number of lasers the current lumencor system can control""" self.message = lumencor_httpcommand(command ='GET CHMAP', ip=self.ip) if self.message['message'][0]=='A': return len(self.message['message'].split(' '))-2 return 0 + def getColor(self): """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) return colors[int(self.laser_id)] + def getIP(self): self.message = lumencor_httpcommand(command = 'GET IP', ip=self.ip) return self.message + def getExtControl(self): """ Return True/False the lasers can be controlled with TTL. @@ -67,6 +73,7 @@ def getExtControl(self): self.message = lumencor_httpcommand(command = 'GET TTLENABLE', ip=self.ip) response = self.message['message'] return response[-1]=='1' + def setExtControl(self, mode): """ Turn on/off external TTL control mode. @@ -76,7 +83,7 @@ def setExtControl(self, mode): else: ttl_enable = '0' self.message = lumencor_httpcommand(command = 'SET TTLENABLE '+ttl_enable,ip=self.ip) - + def getLaserOnOff(self): """ Return True/False the laser is on/off. @@ -116,6 +123,7 @@ def setLaserOnOff(self, on): self.message = lumencor_httpcommand(command = 'SET CH '+self.laser_id+' 0', ip=self.ip) self.on = False print("Turning On/Off", self.on, self.message) + def setPower(self, power_in_mw): """ power_in_mw - The desired laser power in mW. @@ -127,6 +135,7 @@ def setPower(self, power_in_mw): if self.message['message'][0]=='A': return True return False + def shutDown(self): """ Turn the laser off. @@ -134,27 +143,13 @@ def shutDown(self): if self.live: self.setPower(0) self.setLaserOnOff(False) + def getStatus(self): """ Get the status """ return self.live - -# -# Testing -# -if (__name__ == "__main__"): - import time - obj = LumencorLaser(laser_id=0,ip = '192.168.201.200') - if obj.getStatus(): - print(obj.getPowerRange()) - print(obj.getLaserOnOff()) - obj.setLaserOnOff(True) - obj.setPower(20.0) - time.sleep(0.1) - obj.shutDown() - # # The MIT License # From 6e0d0c04d190253998fa5947233e68281d00f3dc Mon Sep 17 00:00:00 2001 From: Hongquan Li Date: Thu, 15 Feb 2024 08:33:16 -0500 Subject: [PATCH 3/4] use the same object to control all the laser lines --- software/control/celesta.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/software/control/celesta.py b/software/control/celesta.py index a45fe8707..f62d3f951 100755 --- a/software/control/celesta.py +++ b/software/control/celesta.py @@ -29,13 +29,12 @@ def __init__(self, **kwds): """ self.on = False self.ip = kwds.get('ip', '192.168.201.200') - self.laser_id = str(kwds.get('laser_id', 0)) [self.pmin, self.pmax] = 0,1000 try: # See if the system returns back the right IP. self.message = self.getIP() assert (self.message['message'] == 'A IP '+self.ip) - assert (int(self.laser_id) self.pmax: power_in_mw = self.pmax - self.message = lumencor_httpcommand(command ='SET CHINT '+self.laser_id+' '+ str(int(power_in_mw)), ip=self.ip) + self.message = lumencor_httpcommand(command ='SET CHINT '+laser_id+' '+ str(int(power_in_mw)), ip=self.ip) if self.message['message'][0]=='A': return True return False @@ -141,8 +141,9 @@ def shutDown(self): Turn the laser off. """ if self.live: - self.setPower(0) - self.setLaserOnOff(False) + for i in range(self.n_lasers): + self.setPower(i,0) + self.setLaserOnOff(i,False) def getStatus(self): """ From fd12453f461eceddfbfc2c4d38c69762e99dbf07 Mon Sep 17 00:00:00 2001 From: Hongquan Li Date: Thu, 15 Feb 2024 10:38:12 -0500 Subject: [PATCH 4/4] integrate celesta control --- software/control/core.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/software/control/core.py b/software/control/core.py index da71de0db..af95e6611 100644 --- a/software/control/core.py +++ b/software/control/core.py @@ -42,6 +42,8 @@ import subprocess +import control.celesta as celesta + class ObjectiveStore: def __init__(self, objectives_dict = OBJECTIVES, default_objective = DEFAULT_OBJECTIVE): self.objectives_dict = objectives_dict @@ -416,20 +418,36 @@ def __init__(self,camera,microcontroller,configurationManager,control_illuminati self.display_resolution_scaling = DEFAULT_DISPLAY_CROP/100 + self.celesta = celesta.CELESTA(ip = '192.168.201.200') + self.activte_celesta_channel = None + # illumination control def turn_on_illumination(self): - self.microcontroller.turn_on_illumination() + if 'Fluorescence' in self.currentConfiguration.name: + self.celesta.setLaserOnOff(self.activte_celesta_channel,True) + else: + self.microcontroller.turn_on_illumination() self.illumination_on = True def turn_off_illumination(self): - self.microcontroller.turn_off_illumination() + if 'Fluorescence' in self.currentConfiguration.name: + self.celesta.setLaserOnOff(self.activte_celesta_channel,False) + 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) else: - self.microcontroller.set_illumination(illumination_source,intensity) + if 'Fluorescence' in self.currentConfiguration.name: + laser_id = illumination_source-20 + print('set active channel to ' + str(laser_id)) + self.activte_celesta_channel = int(laser_id) + print('set intensity') + self.celesta.set_intensity(self.activte_celesta_channel,intensity*10) + else: + self.microcontroller.set_illumination(illumination_source,intensity) def start_live(self): self.is_live = True