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

Pull request for plugin feature #59

Open
jacopoabramo opened this issue Feb 27, 2024 · 9 comments
Open

Pull request for plugin feature #59

jacopoabramo opened this issue Feb 27, 2024 · 9 comments

Comments

@jacopoabramo
Copy link
Collaborator

Before creating a pull request, write documentation on how a plugin is structured, types of possible plugins (widget/controller couple, hardware I presume?) and how to add a plugin to the configuration file.

@jacopoabramo
Copy link
Collaborator Author

@beniroquai forgot to add the tag

@beniroquai
Copy link
Collaborator

Certainly! Thanks for the reminder. How should I do it anyway? Cherry-pick the files I modified from the imswitch/imswitch master and then document the sample plugins?
I am not read-the-docs compatible. Could I document it here? https://openuc2.github.io/docs/ImSwitch/ImSwitchUpdate (with a new page of course)

First Plugin:

https://github.com/openUC2/imswitch-flimlabs

ImSwitch Cookie Cutter (without manager yet)

https://github.com/openUC2/cookiecutter-imswitch

Both use the openUC2 branch, but if we replace openuc2 by imswitch it should work either way. We could have a flag - or long term just imswitch ;)

@jacopoabramo
Copy link
Collaborator Author

We could have a flag - or long term just imswitch ;)

Definetely worth putting just ImSwitch for the plugins.

@jacopoabramo
Copy link
Collaborator Author

Right now the plugin works for controller/widget coupling only right? Not for device managers?

@beniroquai
Copy link
Collaborator

Now this becomes odd, but anyway. I created a new branch that is based on the dev-branch from imswitch/imswitch. I cherry-picked the changes that I made in the openuc2/imswitch master branch to get the plugins work.

The reference plugin so far is the https://github.com/openUC2/imswitch-flimlabs plugin.

What's next?

Right now the plugin works for controller/widget coupling only right? Not for device managers?

you could load cameras if I'm not mistaking. I guess you could not yet create new basemanagers, like a new type of device layer. Haven't fully thought about it yet.

Explanation:

If loading a controller/widget fails, it looks for relevant plugins:

For Widgets:

            try:
                self.controllers[widgetKey] = self.__factory.createController(
                    (getattr(controllers, f'{widgetKey}Controller')
                    if widgetKey != 'Scan' else
                    getattr(controllers, f'{widgetKey}Controller{self.__setupInfo.scan.scanWidgetType}')), widget
                )
            except Exception as e:
                #try to get it from the plugins
                for entry_point in pkg_resources.iter_entry_points(f'imswitch.implugins'):
                    if entry_point.name == f'{widgetKey}_controller':
                        packageController = entry_point.load()
                        self.controllers[widgetKey] = self.__factory.createController(packageController, widget)
                        break
                self.__logger.debug(e)
                raise ValueError(f'No controller found for widget {widgetKey}')

For Managers (in multimanager only):

                try:
                    package = importlib.import_module(
                        pythontools.joinModulePath(f'{currentPackage}.{subManagersPackage}',
                                                managedDeviceInfo.managerName)
                    )
                    manager = getattr(package, managedDeviceInfo.managerName)
                    self._subManagers[managedDeviceName] = manager(
                        managedDeviceInfo, managedDeviceName, **lowLevelManagers)

                except:
                    # try to import from the implugins
                    for entry_point in pkg_resources.iter_entry_points(f'imswitch.implugins.{subManagersPackage}'):
                        manager = entry_point.load()
                        self._subManagers[managedDeviceName] = manager(
                            managedDeviceInfo, managedDeviceName, **lowLevelManagers)

For Controllers

            try:
                self.widgets[widgetKey] = self.factory.createWidget(
                    getattr(widgets, f'{widgetKey}Widget')
                    if widgetKey != 'Scan' else
                    getattr(widgets, f'{widgetKey}Widget{self.viewSetupInfo.scan.scanWidgetType}')
                )
            except:
                # try to get it from the plugins
                for entry_point in pkg_resources.iter_entry_points(f'imswitch.implugins'):
                    if entry_point.name == f'{widgetKey}_widget':
                        packageWidget = entry_point.load()
                        self.widgets[widgetKey] = self.factory.createWidget(packageWidget)
                        break

The commit e47f62f certainly needs some cleanup. Also we would need to document the proper entry-point descriptions (e.g. FooBarController goes with FooBarWidget, etc..)

@jacopoabramo stage is yours ;)

@jacopoabramo
Copy link
Collaborator Author

I think that using the multi-managers as entry-points for device manager is a perfect match. This introduces a level of categorization for which I'm not a fan of, but to keep an imswitch compatible infrastracture I believe this is the way to go.

I believe there are several key factors to take into account to make sure that this works properly, starting from removing the pyqt signals from the presenter-model layers just as we discussed. I'll prepare a project for this, I believe it's the one with the highest priority.

@beniroquai
Copy link
Collaborator

Have you tried yourself already?

@beniroquai
Copy link
Collaborator

One more addition would be a detector plugin. This is only for the records so that we won't loose it:

from imswitch.imcontrol.model.managers.detectors.DetectorManager import DetectorManager, DetectorAction, DetectorNumberParameter
from imswitch.imcontrol.controller.basecontrollers import ImConWidgetController
from imswitch.imcontrol.view.ImConMainView import _DockInfo

import numpy as np
from typing import Any, Dict, List, Optional, Tuple


rightDockInfos = {
            'Autofocus': _DockInfo(name='Autofocus', yPosition=0),
}


class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

'''
class CameraPlugin(ImConWidgetController):
    """Linked to CameraPluginWidget."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
'''

class cameraplugin(DetectorManager):
    def __init__(self, detectorInfo=None, name="Test", **kwargs):
        # Initialize any additional camera-specific setup here
        if detectorInfo is not None:
            self.detectorInfo = detectorInfo
            model = "Test"
            fullShape = [100, 100]
            supportedBinnings = [1]
        else:
            self.detectorInfo = {
                "name": "CameraPlugin",
                "fullShape": [100, 100],
                "supportedBinnings": [1, 2, 4],
                "model": "Test",
                "forAcquisition": True,
            }
            self.detectorInfo = dotdict(self.detectorInfo)
            fullShape = self.detectorInfo.fullShape
            supportedBinnings = self.detectorInfo.supportedBinnings
            model = self.detectorInfo.model
            
        # Prepare parameters
        parameters = {
            'exposure': DetectorNumberParameter(group='Misc', value=100, valueUnits='ms',
                                                editable=True),
            'gain': DetectorNumberParameter(group='Misc', value=1, valueUnits='arb.u.',
                                            editable=True),
            'brightness': DetectorNumberParameter(group='Misc', value=1, valueUnits='arb.u.',
                                                editable=True),
        }

        # Prepare actions
        actions = {
        }
        
        super().__init__(self.detectorInfo, name, fullShape, supportedBinnings, model,
                          parameters=parameters, actions=actions, croppable=True)

    def pixelSizeUm(self) -> List[int]:
        # Return the pixel size in micrometers for Z, Y, X dimensions
        return [1, 1.1, 1.1]  # Example values

    def crop(self, hpos: int, vpos: int, hsize: int, vsize: int) -> None:
        # Implement the cropping logic here
        pass

    def getLatestFrame(self) -> np.ndarray:
        # Return the latest frame captured by the camera
        # This is just a placeholder implementation
        return np.random.rand(self.fullShape[0], self.fullShape[1])  # Example: return a random image

    def getChunk(self) -> np.ndarray:
        # Return a chunk of frames since the last call or flush
        # This is just a placeholder implementation
        return np.random.rand(5, self.fullShape[0], self.fullShape[1])  # Example: return a stack of 5 random images

    def flushBuffers(self) -> None:
        # Flush the internal buffers
        pass

    def startAcquisition(self) -> None:
        # Start image acquisition
        pass

    def stopAcquisition(self) -> None:
        # Stop image acquisition
        pass

    @property
    def pixelSizeUm(self):
        return [1, 1, 1]

@beniroquai
Copy link
Collaborator

So, the Managers are added too. The attempt to integrate the dataclasses into SetupInfo seems a bit more involved for now.
Here are the relevant changes:
https://github.com/openUC2/ImSwitch/blob/master/imswitch/imcontrol/controller/MasterController.py#L68
And here a fully working ImSwitch Plugin which we will publish in the next weeks alongside this opensimmo.github.io. @ranranking 's hard work!! :)
https://github.com/openUC2/imswitch-sim

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants