Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] add system fingerprint based configuration #321

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions fibsem/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,132 @@
import os
from fibsem.config import CONFIG_PATH, DEFAULT_CONFIGURATION_VALUES

# TODO:
# eucentric heights -> requires taking an image?
# reference rotation -> how to get this without having the user move to position?

def system_fingerprint(ip_address: str = "192.168.0.1", manufactuer: str = "Thermo") -> dict:
"""Automatically configure system based on microscope fingerprinting."""

# load default configuration
with open(os.path.join(CONFIG_PATH, "microscope-configuration.yaml"), "r") as f:
config = yaml.safe_load(f)

if manufactuer != "Thermo":
raise ValueError("Only Thermo Fisher microscopes are currently supported for fingerprinting.")

# ip, manufacturer
config["info"]["ip"] = ip_address # default ip (support pc -> microscope pc)
config["info"]["manufacturer"] = manufactuer # default manufacturer

from fibsem.microscope import ThermoMicroscope
from fibsem.structures import SystemSettings, BeamSystemSettings, StageSystemSettings, FibsemDetectorSettings

microscope = ThermoMicroscope()
microscope.connect_to_microscope(config["info"]["ip"])

# get name of system
system_name = microscope.connection.service.system.name
serial_number = microscope.connection.service.system.serial_number
config["info"]["name"] = f"{system_name}-{serial_number}-configuration"

# stage
# enabled, compustage, rotation, tilt
stage_enabled = microscope.connection.specimen.stage.is_installed
compustage_enabled = microscope.connection.specimen.compustage.is_installed
config["stage"]["enabled"] = stage_enabled or compustage_enabled
config["stage"]["rotation"] = stage_enabled and not compustage_enabled
config["stage"]["tilt"] = stage_enabled or compustage_enabled

# rotation reference, rotation_180
if compustage_enabled: # TODO: check if this is accurate
config["stage"]["rotation_reference"] = 0
config["stage"]["rotation_180"] = 0

# otherwise? I think need to get this manually?
# TODO: get the user to move the stage to this position?
# tilt = 0, rotation = same as when loading?

# eucentric height map, [electron, ion] (otherwise we need to take an image to get this?)
eucentric_height_map = {
"Aquilios": [7e-3, 16.5e-3],
"Helios": [4e-3, 16.5e-3],
"Artcis": [4e-3, 16.5e-3],
"Scios": [4e-3, 10.0e-3]
}

# check if any key in system_name
for model in eucentric_height_map:
if model in system_name:
system_model = model

# electron beam
config["electron"]["enabled"] = microscope.connection.beams.electron_beam.is_installed
config["electron"]["column_tilt"] = get_column_tilt(config["info"]["manufacturer"], "electron")
config["electron"]["eucentric_height"] = eucentric_height_map[system_model][0]

# ion beam
# is_installed, column tilt, eucentric_height (req taking image), plasma, plasma gas
config["ion"]["enabled"] = microscope.connection.beams.ion_beam.is_installed
config["ion"]["column_tilt"] = get_column_tilt(config["info"]["manufacturer"], "ion")
config["ion"]["eucentric_height"] = eucentric_height_map[system_model][1]

# plasma enabled
ion_plasma_enabled = False

if config["ion"]["enabled"]:

try:
# throws an
ion_plasma_gas = microscope.connection.beams.ion_beam.source.plasma_gas.value
ion_plasma_enabled = True
print(f"Ion Plasma Available, Plasma Gas: {ion_plasma_gas}")
except Exception as e:
print(f"Exception: {e}")
ion_plasma_gas = "None"
ion_plasma_enabled = False

config["ion"]["plasma"] = ion_plasma_enabled
config["ion"]["plasma_gas"] = ion_plasma_gas

# manipulator
config["manipulator"]["enabled"] = microscope.connection.specimen.manipulator.is_installed
config["manipulator"]["rotation"] = False # always?
config["manipulator"]["tilt"] = False # always?

# gis
gis_enabled = len(microscope.connection.gas.list_all_gis_ports())
multichem_enabled = len(microscope.connection.gas.list_all_multichem_ports())
sputter_coater_enabled = microscope.connection.specimen.sputter_coater.is_installed

config["gis"]["enabled"] = bool(gis_enabled)
config["gis"]["multichem"] = bool(multichem_enabled)
config["gis"]["sputter_coater"] = sputter_coater_enabled

print(f"""System Fingerprint:
Electron Beam:
Enabled: {config["electron"]["enabled"]}
Column Tilt: {config["electron"]["column_tilt"]}
Ion Beam:
Enabled: {config["ion"]["enabled"]}
Column Tilt: {config["ion"]["column_tilt"]}
Plasma: {config["ion"]["plasma"]}
Plasma Gas: {config["ion"]["plasma_gas"]}
Stage:
Enabled: {config["stage"]["enabled"]}
Rotation: {config["stage"]["rotation"]}
Tilt: {config["stage"]["tilt"]}
Rotation Reference: {config["stage"]["rotation_reference"]}
Rotation 180: {config["stage"]["rotation_180"]}
Manipulator:
Enabled: {config["manipulator"]["enabled"]}
GIS:
Enabled: {config["gis"]["enabled"]}
Multichem: {config["gis"]["multichem"]}
Sputter Coater: {config["gis"]["sputter_coater"]}
""")

return config


def get_column_tilt(manufacturer: str, beam: str) -> int:
Expand Down
21 changes: 13 additions & 8 deletions fibsem/microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,10 @@ def __init__(self, system_settings: SystemSettings = None):
self.experiment = FibsemExperiment()

# logging
logging.debug({"msg": "create_microscope_client", "system_settings": system_settings.to_dict()})
if system_settings is not None:
system_settings = system_settings.to_dict()
logging.debug({"msg": "create_microscope_client", "system_settings": system_settings})


def reconnect(self):
if not hasattr(self, "system"):
Expand Down Expand Up @@ -808,18 +811,20 @@ def connect_to_microscope(self, ip_address: str, port: int = 7520) -> None:
logging.info(f"Microscope client connected to [{ip_address}:{port}]")

# system information
self.system.info.model = self.connection.service.system.name
self.system.info.serial_number = self.connection.service.system.serial_number
self.system.info.hardware_version = self.connection.service.system.version
self.system.info.software_version = self.connection.service.autoscript.client.version
info = self.system.info
logging.info(f"Microscope client connected to model {info.model} with serial number {info.serial_number} and software version {info.software_version}.")
if self.system is not None:
self.system.info.model = self.connection.service.system.name
self.system.info.serial_number = self.connection.service.system.serial_number
self.system.info.hardware_version = self.connection.service.system.version
self.system.info.software_version = self.connection.service.autoscript.client.version
info = self.system.info
logging.info(f"Microscope client connected to model {info.model} with serial number {info.serial_number} and software version {info.software_version}.")

# autoscript information
logging.info(f"Autoscript Client: {self.connection.service.autoscript.client.version}")
logging.info(f"Autoscript Server: {self.connection.service.autoscript.server.version}")

self.reset_beam_shifts()
if self.system is not None: # tmp
self.reset_beam_shifts()

def acquire_image(self, image_settings:ImageSettings) -> FibsemImage:
"""
Expand Down