Adding support for more devices

There are three main device types that ImSwitch’s hardware control module supports: detectors, lasers and positioners. In order to add support for a new detector, laser or positioner, a corresponding device manager class must be implemented in ImSwitch’s code.

How device managers are implemented

Detector support is implemented in device manager classes derived from the abstract base class DetectorManager. The corresponding parent class for lasers is LaserManager, and for positioners it is PositionerManager. These derived classes are placed in the detectors, lasers and positioners sub-modules respectively in the imswitch.imcontrol.model.managers module.

The required constructor signature for the device managers is __init__(deviceInfo, name, **lowLevelManagers). deviceInfo is the DetectorInfo, LaserInfo or PositionerInfo object which represents the device’s entry in the setup file (see the hardware control setup page for further information). Inside it, the managerProperties dict field may contain manager-specific properties. name is a unique name that is used to identify the device, which is defined by the key of the device’s entry in the setup file. lowLevelManagers is a dict containing objects that facilitate low-level device interaction, which are documented here. Note that super().__init__ has a different signature, depending on which base class is used.

When creating a new device manager, you will need to implement all the abstract methods and properties defined in the base class. You should avoid overriding non-abstract properties. Overriding non-abstract methods is generally fine, but you should make sure that they continue to work as expected. The device manager class must be placed in a .py file with the same name as the class, in the appropriate location as outlined above. No other action is required for the device manager to be available to use; it will automatically be managed by a multi-manager as outlined in the paper.

You can find a simple example of a positioner manager implementation here.

Base class documentation

DetectorManager

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorManager(detectorInfo, name: str, fullShape: Tuple[int, int], supportedBinnings: List[int], model: str, *, parameters: Optional[Dict[str, imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter]] = None, actions: Optional[Dict[str, imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorAction]] = None, croppable: bool = True)

Abstract base class for managers that control detectors. Each type of detector corresponds to a manager derived from this class.

abstract __init__(detectorInfo, name: str, fullShape: Tuple[int, int], supportedBinnings: List[int], model: str, *, parameters: Optional[Dict[str, imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter]] = None, actions: Optional[Dict[str, imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorAction]] = None, croppable: bool = True) None
Parameters
  • detectorInfo – See setup file documentation.

  • name – The unique name that the device is identified with in the setup file.

  • fullShape – Maximum image size as a tuple (width, height).

  • supportedBinnings – Supported binnings as a list.

  • model – Detector device model name.

  • parameters – Parameters to make available to the user to view/edit.

  • actions – Actions to make available to the user to execute.

  • croppable – Whether the detector image can be cropped.

property actions: Dict[str, imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorAction]

Dictionary of available actions.

property binning: int

Current binning.

abstract crop(hpos: int, vpos: int, hsize: int, vsize: int) None

Crop the frame read out by the detector.

property croppable: bool

Whether the detector supports frame cropping.

finalize() None

Close/cleanup detector.

abstract flushBuffers() None

Flushes the detector buffers so that getChunk starts at the last frame captured at the time that this function was called.

property forAcquisition: bool

Whether the detector is used for acquisition.

property forFocusLock: bool

Whether the detector is used for focus lock.

property frameStart: Tuple[int, int]

Position of the top left corner of the current frame as a tuple (x, y).

property fullShape: Tuple[int, ...]

Maximum image size as a tuple (width, height, ...).

abstract getChunk() numpy.ndarray

Returns the frames captured by the detector since getChunk was last called, or since the buffers were last flushed (whichever happened last). The returned object is a numpy array of shape (numFrames, height, width).

abstract getLatestFrame() numpy.ndarray

Returns the frame that represents what the detector currently is capturing. The returned object is a numpy array of shape (height, width).

property image: numpy.ndarray

Latest LiveView image.

property model: str

Detector model name.

property name: str

Unique detector name, defined in the detector’s setup info.

property parameters: Dict[str, imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter]

Dictionary of available parameters.

abstract property pixelSizeUm: List[int]

The pixel size in micrometers, in 3D, in the format [Z, Y, X]. Non-scanned Z set to 1.

property scale: List[int]

The pixel sizes in micrometers, all axes, in the format high dim to low dim (ex. […, ‘Z’, ‘Y’, ‘X’]). Override in managers handling >3 dim images (e.g. APDManager).

setBinning(binning: int) None

Sets the detector’s binning.

setParameter(name: str, value: Any) Dict[str, imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter]

Sets a parameter value and returns the updated list of parameters. If the parameter doesn’t exist, i.e. the parameters field doesn’t contain a key with the specified parameter name, an AttributeError will be raised.

property shape: Tuple[int, ...]

Current image size as a tuple (width, height, ...).

abstract startAcquisition() None

Starts image acquisition.

abstract stopAcquisition() None

Stops image acquisition.

property supportedBinnings: List[int]

Supported binnings as a list.

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorAction(group: str, func: callable)

An action that is made available for the user to execute.

func: callable

The function that is called when the action is executed.

group: str

The group to place the action in (does not need to be pre-defined).

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter(group: str, value: Any, editable: bool)

Abstract base class for detector parameters that are made available for the user to view/edit.

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorNumberParameter(group: str, value: float, editable: bool, valueUnits: str)

Bases: imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter

A detector parameter with a numerical value.

editable: bool

Whether it is possible to edit the value of the parameter.

group: str

The group to place the parameter in (does not need to be pre-defined).

value: float

The value of the parameter.

valueUnits: str

Parameter value units, e.g. “nm” or “fps”.

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorListParameter(group: str, value: str, editable: bool, options: List[str])

Bases: imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter

A detector parameter with a value from a list of options.

editable: bool

Whether it is possible to edit the value of the parameter.

group: str

The group to place the parameter in (does not need to be pre-defined).

options: List[str]

The available values to pick from.

value: str

The value of the parameter.

LaserManager

class imswitch.imcontrol.model.managers.lasers.LaserManager.LaserManager(laserInfo, name: str, isBinary: bool, valueUnits: str, valueDecimals: int, isModulated: bool = False)

Abstract base class for managers that control lasers. Each type of laser corresponds to a manager derived from this class.

abstract __init__(laserInfo, name: str, isBinary: bool, valueUnits: str, valueDecimals: int, isModulated: bool = False) None
Parameters
  • laserInfo – See setup file documentation.

  • name – The unique name that the device is identified with in the setup file.

  • isBinary – Whether the laser can only be turned on and off, and its value cannot be changed.

  • valueUnits – The units of the laser value, e.g. “mW” or “V”.

  • valueDecimals – How many decimals are accepted in the laser value.

  • isModulated – Whether the laser can be frequency modulated.

finalize() None

Close/cleanup laser.

property freqRangeInit: int

The initial frequency of the laser modulation.

property freqRangeMax: int

The minimum frequency of the laser modulation.

property freqRangeMin: int

The minimum frequency of the laser modulation.

property isBinary: bool

Whether the laser can only be turned on and off, and its value cannot be changed.

property isModulated: bool

Whether the laser supports frequency modulation.

property name: str

Unique laser name, defined in the laser’s setup info.

abstract setEnabled(enabled: bool) None

Sets whether the laser is enabled.

setModulationDutyCycle(dutyCycle: int) None

Sets the laser modulation duty cycle.

setModulationEnabled(enabled: bool) None

Sets wether the laser frequency modulation is enabled.

setModulationFrequency(frequency: int) None

Sets the laser modulation frequency.

setScanModeActive(active: bool) None

Sets whether the laser should be in scan mode (if the laser supports it).

abstract setValue(value: Union[int, float]) None

Sets the value of the laser.

property valueDecimals

How many decimals are accepted in the laser value.

property valueRangeMax: float

The maximum value that the laser can be set to.

property valueRangeMin: float

The minimum value that the laser can be set to.

property valueRangeStep: float

The default step size of the value range that the laser can be set to.

property valueUnits: str

The units of the laser value, e.g. “mW” or “V”.

property wavelength: int

The wavelength of the laser.

PositionerManager

class imswitch.imcontrol.model.managers.positioners.PositionerManager.PositionerManager(positionerInfo, name: str, initialPosition: Dict[str, float])

Abstract base class for managers that control positioners. Each type of positioner corresponds to a manager derived from this class.

abstract __init__(positionerInfo, name: str, initialPosition: Dict[str, float])
Parameters
  • positionerInfo – See setup file documentation.

  • name – The unique name that the device is identified with in the setup file.

  • initialPosition – The initial position for each axis. This is a dict in the format { axis: position }.

property axes: List[str]

The list of axes that are controlled by this positioner.

finalize() None

Close/cleanup positioner.

property forPositioning: bool

Whether the positioner is used for manual positioning.

property forScanning: bool

Whether the positioner is used for scanning.

abstract move(dist: float, axis: str)

Moves the positioner by the specified distance and returns the new position. Derived classes will update the position field manually. If the positioner controls multiple axes, the axis must be specified.

property name: str

Unique positioner name, defined in the positioner’s setup info.

property position: Dict[str, float]

The position of each axis. This is a dict in the format { axis: position }.

property resetOnClose: bool

Whether the positioner should be reset to 0-position upon closing.

abstract setPosition(position: float, axis: str)

Adjusts the positioner to the specified position and returns the new position. Derived classes will update the position field manually. If the positioner controls multiple axes, the axis must be specified.

Available low-level managers

lowLevelManagers[‘nidaqManager’]

class imswitch.imcontrol.model.managers.NidaqManager.NidaqManager(setupInfo)

For interaction with NI-DAQ hardware interfaces.

setAnalog(target, voltage, min_val=- 1, max_val=1)

Function to set the analog channel to a specific target to a certain voltage

setDigital(target, enable)

Function to set the digital line to a specific target to either “high” or “low” voltage

lowLevelManagers[‘rs232sManager’]

class imswitch.imcontrol.model.managers.RS232sManager.RS232sManager(rs232deviceInfos, **lowLevelManagers)

RS232sManager is an interface for dealing with RS232 devices. It is a MultiManager for RS232 devices.

RS232Manager instances for individual RS232 devices can be accessed by rs232sManager['your_rs232_device_name'].

class imswitch.imcontrol.model.managers.rs232.RS232Manager.RS232Manager(rs232Info, name, **_lowLevelManagers)

A general-purpose RS232 manager that together with a general-purpose RS232Driver interface can handle an arbitrary RS232 communication channel, with all the standard serial communication protocol parameters as defined in the hardware control configuration.

Manager properties:

  • port

  • encoding

  • recv_termination

  • send_termination

  • baudrate

  • bytesize

  • parity

  • stopbits

  • rtscts

  • dsrdtr

  • xonxoff

query(arg: str) str

Sends the specified command to the RS232 device and returns a string encoded from the received bytes.

write(arg: str)

Sends the specified command to the RS232 device.