Extending fibsemOS
Generated from
docs/developers/extending.mdin fibsem-os at build time. Edit it there; changes here are overwritten.
fibsemOS has four supported extension points, listed here in increasing order of integration: a script, a plugin, a workflow task, and a microscope backend. Each section describes the interface, the reference implementation, and the constraints that are not obvious from the code. Other approaches are possible; open an issue before relying on internals that are not listed here.
Choosing a route
| Goal | Mechanism | Where it runs |
|---|---|---|
| Read or modify experiment data, offline | A Python script using Experiment.load() | Anywhere; no application |
| Drive the microscope for an acquisition sweep or a one-off procedure | A Python script using utils.setup_session() | Anywhere; no application |
| Run a procedure against the experiment the application has open | A user script in the scripts folder, run from Tools → Scripts | Inside the application |
| Add a milling pattern, a milling strategy or a workflow task that others can install | A plugin package with entry points | Inside the application, discovered at start |
| Add a step to the AutoLamella workflow with supervision and history | A workflow task (as a plugin, or in the repository) | Inside the workflow queue |
| Support an instrument fibsemOS does not drive yet | A FibsemMicroscope implementation | The library; everything else is built on it |
Scripts
Against the library
from fibsem import utils, acquire
from fibsem.structures import BeamType, ImageSettings
microscope, settings = utils.setup_session(manufacturer="Demo")
image = acquire.acquire_image(microscope, ImageSettings(hfw=80e-6, beam_type=BeamType.ION))setup_session() connects using a configuration file (the shipped Demo
configuration by default) and returns the microscope and its settings. The
fibsem.acquire, fibsem.imaging and fibsem.milling modules provide the
higher-level operations, and every FibsemMicroscope method is available
directly. Use acquire.acquire_image(microscope, settings) rather than the
microscope’s own acquire_image: the method returns the raw frame and
ignores save=True; the module function applies autocontrast and gamma and
writes the file.
Experiment.load() gives access to an experiment’s lamellae, their history
and the summary dataframes. SCRIPTING.md covers this
in full, with examples that the test suite executes against real data.
Inside the application
AutoLamella can run a .py file from the scripts folder against the open
experiment, from Tools → Scripts → Manage scripts…. A script is one
module-level function:
"""Export the experiment summary to CSV.""" # the first line becomes the tooltip
def run(ctx):
out = ctx.path / "summary.csv"
ctx.experiment.experiment_summary_dataframe().to_csv(out, index=False)
return outctx is a ScriptContext (fibsem/applications/autolamella/scripting.py)
carrying the experiment, its path, a logger and, if the script declares
uses_microscope = True, the microscope. Declaring writes = True saves
the experiment afterwards. Return values are displayed: a string as a
notification, a DataFrame as a table, a path by opening its folder. Use
ctx rather than the UI object; the UI’s internals change between
versions, and its experiment attribute is replaced on load, so a cached
reference becomes stale without error.
Microscope scripts run on a background thread, must not modify
ctx.experiment, and must call ctx.raise_if_cancelled() between steps for
Stop to take effect. Nothing validates what a script does to the hardware.
The Running scripts from the GUI
and Microscope scripts sections give
the full contract; examples/scripts/ contains three working examples.
Plugins
fibsemOS loads three entry-point groups at start: fibsem.patterns,
fibsem.strategies and fibsem.tasks. A plugin is a Python package that
declares one or more of these in its pyproject.toml and is installed into
the same environment as fibsemOS. No registration calls or files in the
fibsem tree are needed; the application discovers it on the next start.
The fibsem-plugin-example
repository is a template covering all three groups. It includes tests that
check the contract, a CI workflow that installs against fibsemOS main so
breaking changes are detected early, and a README describing how to rename
it. Its “When nothing shows up” section covers the diagnostic steps.
Each group contributes one kind of object:
- A pattern (
fibsem.patterns): a subclass ofBasePatternfromfibsem.milling.patterning.patterns2whosedefine()returns the shapes to mill. It appears in the pattern list of every milling stage. - A strategy (
fibsem.strategies): how a pattern is milled, for example in N passes. It appears in the strategy list of every stage. - A task (
fibsem.tasks): anAutoLamellaTaskwith its configuration class, as described in the next section. It appears in the Add Task dialog.
Three constraints on loading. Each produces no error when violated, so the example’s tests check them:
- Pattern modules are imported while
fibsem.milling.baseis only partly initialised. ImportBasePatternfrompatterns2, not from thefibsem.milling.patterningpackage, and import nothing that depends onfibsem.milling.base, which includes everything underfibsem.applications. A pattern and a task defined in the same module breaks the pattern. Strategies and tasks have no such restriction. - A plugin that fails to import is absent. The application starts
without it and the class does not appear in the list. The reason is
recorded in the log and in the Plugins panel (Tools → Plugins…, or
fibsem-cli plugins), which lists every declared entry point and its outcome, including entry points shadowed by a built-in of the same name.fibsem/plugins/loader.pyis the loader and documents the records it keeps. - Changes to
pyproject.tomltake effect only after reinstalling. Entry points are read from the installed metadata, so runpip install -e .again after editing them. Changes to Python files need no reinstall.
Distances in configuration forms are stored in metres and scaled for
display. A distance field without scale metadata renders as 0.000 m.
Spread DEFAULT_DISTANCE_METADATA from fibsem.milling.properties into
distance fields, as the built-in patterns do; the example’s tests check this
as well.
Workflow tasks
A task is one step applied to one lamella. Subclass AutoLamellaTask
(fibsem/applications/autolamella/workflows/tasks/base.py) with a matching
AutoLamellaTaskConfig, and register both with register_task() in the
repository or through the fibsem.tasks entry point from a plugin.
fiducial.py is a representative task module of moderate complexity.
Four contracts:
- Override
_run(), notrun().run()is the lifecycle wrapper: it callspre_task(), the hooks,_run()andpost_task(). Overridingrun()loses the task’s state, its history entry and every hook, without an error. - Questions go through
ask()(workflows/interaction.py). Build aRequestcarrying everything needed to answer it and block on the responder. Do not access widgets from the workflow thread. This interface is what allows the GUI, the operator and a remote agent to answer the same question, and it is why supervised and unsupervised runs share one code path. - Record outputs on the task’s history entry (
task_state.outputs, mapping role to files), and write images underlamella.path. The Review panel and the reports read from there; unrecorded files are not shown, and files written elsewhere are lost when the experiment is copied off the microscope. - Cancellation is cooperative. Stop sets an event. A task that does not
call
self._check_for_abort(), or a strategy that does not checkstop_event, cannot be stopped.
task_type is written into protocol files and is therefore permanent:
renaming it orphans every protocol that names it. Configuration classes are
serialised into the protocol as well, so they should contain only plain
types that round-trip through YAML.
Supporting a microscope
The interface is FibsemMicroscope (fibsem/microscope.py), an abstract
class covering acquisition, movement, milling and state. Once a backend
implements it and is registered, the workflows, UI and server work
unchanged.
- Reference implementations.
DemoMicroscopeinfibsem/microscopes/simulator.pyis the complete, hardware-free reference.fibsem/microscopes/tescan.pyshows a vendor SDK behind the same interface.microscopes/zeiss.pyis an empty placeholder awaiting a SerialFIB migration; a Zeiss backend should be built there rather than in a new file. - Registration. Implementing the class is not sufficient. The
manufacturer dispatch is hard-coded in four places, and a missing entry
raises
NotImplementedErrorat connection time, not at import:fibsem/manufacturers.py(the constant and alias),setup_session()infibsem/utils.py(the branch that constructs the class),fibsem/configuration.py(the accepted manufacturers), andfibsem/guided_setup.py(MANUFACTURERS, and aMicroscopeModelper instrument, which is what the first-run wizard offers). - Configuration. Instruments are described by a YAML file in
fibsem/config/;fibsem-generate-configscaffolds one. For a manufacturer it does not know, scaffold a Demo configuration and edit it. - Verification. Connect with
utils.setup_session()and run the tests that exercise the Demo through the same interface (tests/test_acquire.py,tests/test_movement.py,tests/test_microscope.py), then connect through the application. Not every abstract method needs to work at first: acquisition and stage movement are the prerequisites for the rest, and unimplemented methods can raise until their subsystem is addressed.
Fluorescence microscopes
Subclass FluorescenceMicroscope (fibsem/fm/microscope.py), which covers
the objective, the filter set, the camera and acquisition.
SimulatedFluorescenceMicroscope in fibsem/microscopes/simulator.py is
the hardware-free reference, and the Thermo Fisher and Odemis backends in
fibsem/fm/ are the two hardware implementations. This interface is under
active development and is expected to change; open an issue before building
on it.
Segmentation models
Models live under fibsem/segmentation/. SegmentationModelHuggingFace
loads checkpoints from the Hugging Face Hub, and the local-checkpoint paths
are beside it. A general model library (sidecar metadata, local and Hub
resolution) is planned but not implemented. For now, match the loading
interface of the existing models; this area is expected to change.