diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a40a4c92..de47d605 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,25 +10,30 @@ jobs: contents: read runs-on: ubuntu-22.04 steps: + # Our workflows were running out of memory. We don't need most of the pre-installed machinery in the + # github ubuntu image, so remove it using this helper action. + # See: https://github.com/marketplace/actions/free-disk-space-ubuntu + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: true + dotnet: true + haskell: true + large-packages: true + docker-images: true + swap-storage: true - name: Checkout the Squid repo uses: actions/checkout@v4 - name: Run the setup script for Ubuntu 22.04 - run: ./setup_22.04.sh + run: ./setup_22.04.sh -r=${{ github.workspace }} working-directory: ./software - env: - SRC_ROOT: "${{ runner.temp }}" - name: Run the cuda setup script run: ./setup_cuda_22.04.sh working-directory: ./software - env: - SRC_ROOT: "${{ runner.temp }}" - # NOTE(imo): Our setup script checks out the repository, so we actually check it out twice. - # Once to get the script (using actions/checkout@v4), then again via the script. We want to do - # all of our actually testing in the one the script checks out, though, so make sure we set SRC_ROOT - # properly and then use the same working directory for running within the setup script's checkout - - name: "TEMPORARY: copy a valid config into the repo software root" + - name: "Copy a valid config into the repo software root" run: cp configurations/configuration_Squid+.ini . - working-directory: "${{ runner.temp }}/Squid/software" + working-directory: ./software - name: Run the tests run: python3 -m pytest - working-directory: "${{ runner.temp }}/Squid/software" \ No newline at end of file + working-directory: ./software diff --git a/software/control/celesta.py b/software/control/celesta.py index 73d2ebe0..ee2705c7 100755 --- a/software/control/celesta.py +++ b/software/control/celesta.py @@ -8,7 +8,7 @@ import urllib.request import traceback from squid.abc import LightSource -from control.microscope import LightSourceType, IntensityControlMode, ShutterControlMode +from control.lighting import LightSourceType, IntensityControlMode, ShutterControlMode import squid.logging diff --git a/software/control/gui_hcs.py b/software/control/gui_hcs.py index 3252aa69..dcce7aaa 100644 --- a/software/control/gui_hcs.py +++ b/software/control/gui_hcs.py @@ -1,6 +1,8 @@ # set QT_API environment variable import os +import control.lighting + os.environ["QT_API"] = "pyqt5" import serial import time @@ -21,7 +23,7 @@ import squid.config import squid.stage.utils import control.microscope -from control.microscope import LightSourceType, IntensityControlMode, ShutterControlMode, IlluminationController +from control.lighting import LightSourceType, IntensityControlMode, ShutterControlMode, IlluminationController log = squid.logging.get_logger(__name__) @@ -325,7 +327,7 @@ def loadSimulationObjects(self): self.camera_focus = camera_fc.Camera_Simulation() if USE_LDI_SERIAL_CONTROL: self.ldi = serial_peripherals.LDI_Simulation() - self.illuminationController = control.microscope.IlluminationController( + self.illuminationController = control.lighting.IlluminationController( self.microcontroller, self.ldi.intensity_mode, self.ldi.shutter_mode, LightSourceType.LDI, self.ldi ) self.camera = camera.Camera_Simulation(rotate_image_angle=ROTATE_IMAGE_ANGLE, flip_image=FLIP_IMAGE) diff --git a/software/control/lighting.py b/software/control/lighting.py new file mode 100644 index 00000000..ba2c84f2 --- /dev/null +++ b/software/control/lighting.py @@ -0,0 +1,127 @@ +from enum import Enum + + +class LightSourceType(Enum): + SquidLED = 0 + SquidLaser = 1 + LDI = 2 + CELESTA = 3 + VersaLase = 4 + SCI = 5 + + +class IntensityControlMode(Enum): + SquidControllerDAC = 0 + Software = 1 + + +class ShutterControlMode(Enum): + TTL = 0 + Software = 1 + + +class IlluminationController: + def __init__( + self, microcontroller, intensity_control_mode, shutter_control_mode, light_source_type=None, light_source=None + ): + self.microcontroller = microcontroller + self.intensity_control_mode = intensity_control_mode + self.shutter_control_mode = shutter_control_mode + self.light_source_type = light_source_type + self.light_source = light_source + self.channel_mappings_TTL = { + 405: 11, + 470: 12, + 488: 12, + 545: 14, + 550: 14, + 555: 14, + 561: 14, + 638: 13, + 640: 13, + 730: 15, + 735: 15, + 750: 15, + } + + self.channel_mappings_software = {} + self.is_on = {} + self.intensity_settings = {} + self.current_channel = None + + if self.light_source_type is not None: + self.configure_light_source() + + def configure_light_source(self): + self.light_source.initialize() + 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 + 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]) + + def set_intensity_control_mode(self, mode): + self.light_source.set_intensity_control_mode(mode) + self.intensity_control_mode = mode + + def get_intensity_control_mode(self): + mode = self.light_source.get_intensity_control_mode() + if mode is not None: + self.intensity_control_mode = mode + return mode + + def set_shutter_control_mode(self, mode): + self.light_source.set_shutter_control_mode(mode) + self.shutter_control_mode = mode + + def get_shutter_control_mode(self): + mode = self.light_source.get_shutter_control_mode() + if mode is not None: + self.shutter_control_mode = mode + return mode + + def get_intensity(self, channel): + if self.intensity_control_mode == IntensityControlMode.Software: + intensity = self.light_source.get_intensity(self.channel_mappings_software[channel]) + self.intensity_settings[channel] = intensity + return intensity # 0 - 100 + + def turn_on_illumination(self, channel=None): + if channel is None: + channel = self.current_channel + + 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: + # self.microcontroller.set_illumination(self.channel_mappings_TTL[channel], self.intensity_settings[channel]) + self.microcontroller.turn_on_illumination() + + self.is_on[channel] = True + + def turn_off_illumination(self, channel=None): + if channel is None: + channel = self.current_channel + + if self.shutter_control_mode == ShutterControlMode.Software: + self.light_source.set_shutter_state(self.channel_mappings_software[channel], on=False) + elif self.shutter_control_mode == ShutterControlMode.TTL: + self.microcontroller.turn_off_illumination() + + self.is_on[channel] = False + + def set_current_channel(self, channel): + self.current_channel = channel + + def set_intensity(self, channel, intensity): + if self.intensity_control_mode == IntensityControlMode.Software: + if intensity != self.intensity_settings[channel]: + 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) + + def get_shutter_state(self): + return self.is_on + + def close(self): + self.light_source.shut_down() diff --git a/software/control/microscope.py b/software/control/microscope.py index a90533f0..7ffb5fdd 100644 --- a/software/control/microscope.py +++ b/software/control/microscope.py @@ -1,6 +1,4 @@ import serial -import time -from enum import Enum from PyQt5.QtCore import QObject @@ -166,129 +164,3 @@ def close(self): self.microcontroller.close() if USE_ZABER_EMISSION_FILTER_WHEEL or USE_OPTOSPIN_EMISSION_FILTER_WHEEL: self.emission_filter_wheel.close() - - -class LightSourceType(Enum): - SquidLED = 0 - SquidLaser = 1 - LDI = 2 - CELESTA = 3 - VersaLase = 4 - SCI = 5 - - -class IntensityControlMode(Enum): - SquidControllerDAC = 0 - Software = 1 - - -class ShutterControlMode(Enum): - TTL = 0 - Software = 1 - - -class IlluminationController: - def __init__( - self, microcontroller, intensity_control_mode, shutter_control_mode, light_source_type=None, light_source=None - ): - self.microcontroller = microcontroller - self.intensity_control_mode = intensity_control_mode - self.shutter_control_mode = shutter_control_mode - self.light_source_type = light_source_type - self.light_source = light_source - self.channel_mappings_TTL = { - 405: 11, - 470: 12, - 488: 12, - 545: 14, - 550: 14, - 555: 14, - 561: 14, - 638: 13, - 640: 13, - 730: 15, - 735: 15, - 750: 15, - } - - self.channel_mappings_software = {} - self.is_on = {} - self.intensity_settings = {} - self.current_channel = None - - if self.light_source_type is not None: - self.configure_light_source() - - def configure_light_source(self): - self.light_source.initialize() - 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 - 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]) - - def set_intensity_control_mode(self, mode): - self.light_source.set_intensity_control_mode(mode) - self.intensity_control_mode = mode - - def get_intensity_control_mode(self): - mode = self.light_source.get_intensity_control_mode() - if mode is not None: - self.intensity_control_mode = mode - return mode - - def set_shutter_control_mode(self, mode): - self.light_source.set_shutter_control_mode(mode) - self.shutter_control_mode = mode - - def get_shutter_control_mode(self): - mode = self.light_source.get_shutter_control_mode() - if mode is not None: - self.shutter_control_mode = mode - return mode - - def get_intensity(self, channel): - if self.intensity_control_mode == IntensityControlMode.Software: - intensity = self.light_source.get_intensity(self.channel_mappings_software[channel]) - self.intensity_settings[channel] = intensity - return intensity # 0 - 100 - - def turn_on_illumination(self, channel=None): - if channel is None: - channel = self.current_channel - - 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: - # self.microcontroller.set_illumination(self.channel_mappings_TTL[channel], self.intensity_settings[channel]) - self.microcontroller.turn_on_illumination() - - self.is_on[channel] = True - - def turn_off_illumination(self, channel=None): - if channel is None: - channel = self.current_channel - - if self.shutter_control_mode == ShutterControlMode.Software: - self.light_source.set_shutter_state(self.channel_mappings_software[channel], on=False) - elif self.shutter_control_mode == ShutterControlMode.TTL: - self.microcontroller.turn_off_illumination() - - self.is_on[channel] = False - - def set_current_channel(self, channel): - self.current_channel = channel - - def set_intensity(self, channel, intensity): - if self.intensity_control_mode == IntensityControlMode.Software: - if intensity != self.intensity_settings[channel]: - 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) - - def get_shutter_state(self): - return self.is_on - - def close(self): - self.light_source.shut_down() diff --git a/software/control/serial_peripherals.py b/software/control/serial_peripherals.py index ac480634..e5cd8e41 100644 --- a/software/control/serial_peripherals.py +++ b/software/control/serial_peripherals.py @@ -3,7 +3,7 @@ import time from typing import Tuple, Optional import struct -from control.microscope import LightSourceType, IntensityControlMode, ShutterControlMode +from control.lighting import LightSourceType, IntensityControlMode, ShutterControlMode from control._def import * from squid.abc import LightSource diff --git a/software/setup_22.04.sh b/software/setup_22.04.sh index 71073247..422fa081 100755 --- a/software/setup_22.04.sh +++ b/software/setup_22.04.sh @@ -6,15 +6,28 @@ if [[ -n "$TRACE" ]]; then set -x fi -if [[ -z "$SRC_ROOT" ]]; then - readonly SRC_ROOT="$HOME/Desktop" -fi -echo "Using SRC_ROOT=$SRC_ROOT" +SQUID_REPO_PATH="$HOME/Desktop/Squid" + +for i in "$@"; do + case $i in + -r=*|--repo_path=*) + SQUID_REPO_PATH="$(cd "${i#*=}" && pwd)" + shift + ;; + -*|--*) + echo "Unknown option $i" + exit 1 + ;; + *) + ;; + esac +done + +echo "Using SQUID_REPO_PATH='${SQUID_REPO_PATH}'" -mkdir -p "$SRC_ROOT" readonly SQUID_REPO_HTTP="https://github.com/Cephla-Lab/Squid.git" -readonly SQUID_REPO_NAME="Squid" -readonly SQUID_SOFTWARE_ROOT="$SRC_ROOT/$SQUID_REPO_NAME/software" +readonly SQUID_SOFTWARE_ROOT="${SQUID_REPO_PATH}/software" +readonly SQUID_REPO_PATH_PARENT="$(dirname "${SQUID_REPO_PATH}")" readonly DAHENG_CAMERA_DRIVER_ROOT="$SQUID_SOFTWARE_ROOT/drivers and libraries/daheng camera/Galaxy_Linux-x86_Gige-U3_32bits-64bits_1.2.1911.9122" readonly DAHENG_CAMERA_DRIVER_API_ROOT="$SQUID_SOFTWARE_ROOT/drivers and libraries/daheng camera/Galaxy_Linux_Python_1.0.1905.9081/api" readonly TOUPCAM_UDEV_RULE_PATH="$SQUID_SOFTWARE_ROOT/drivers and libraries/toupcam/linux/udev/99-toupcam.rules" @@ -26,10 +39,17 @@ sudo apt install python3-pip -y sudo apt install python3-pyqtgraph python3-pyqt5 -y sudo apt install python3-pyqt5.qtsvg -# clone the repo sudo apt-get install git -y -cd "$SRC_ROOT" -git clone "$SQUID_REPO_HTTP" "$SQUID_REPO_NAME" +## clone the repo if we don't already have it. +# No matter, make sure the repo's parent dir is there +mkdir -p "${SQUID_REPO_PATH_PARENT}" +if [[ ! -d "${SQUID_REPO_PATH}" ]]; then + git clone "$SQUID_REPO_HTTP" "${SQUID_REPO_PATH}" +else + echo "Using existing repo at '${SQUID_REPO_PATH}' at HEAD=$(cd "${SQUID_REPO_PATH}" && git rev-parse HEAD)" +fi + + cd "$SQUID_SOFTWARE_ROOT" mkdir -p "$SQUID_SOFTWARE_ROOT/cache" @@ -49,3 +69,5 @@ sudo cp "$TOUPCAM_UDEV_RULE_PATH" /etc/udev/rules.d # enable access to serial ports without sudo sudo usermod -aG dialout $USER + +sudo apt autoremove -y diff --git a/software/setup_cuda_22.04.sh b/software/setup_cuda_22.04.sh index 153b8f86..b0047d2b 100755 --- a/software/setup_cuda_22.04.sh +++ b/software/setup_cuda_22.04.sh @@ -9,3 +9,5 @@ sudo apt-get -y install cuda pip install cuda-python pip install cupy-cuda12x pip3 install torch torchvision torchaudio + +sudo apt autoremove -y