Component Framework
Everything under hSI that manages a piece of the acquisition is a component: an object
derived from scanimage.interfaces.Component. The components live in the
scanimage.components package. Understanding the base class pays off, because it explains
behavior you will run into in every component - why some properties can be changed while
focusing and others cannot, why numInstances matters, and where the Tiff header values
come from.
classdef Component < scanimage.interfaces.Class & most.Model & dabs.resources.SIComponent
most.Modelprovides the property metadata and header machinery.dabs.resources.SIComponentmakes the component a named resource, which is how components findhSIand how the resource store finds components.
The component list
scanimage.SI constructs its components in its constructor and exposes each as an
immutable handle property.
Handle |
Class |
Responsibility |
|---|---|---|
|
|
Owns the coordinate system tree and its persistence. |
|
|
Imaging ROIs, resolution, zoom, frame rates. |
|
|
The active imaging system. |
|
|
Channel display / save selection, offsets, input ranges, averaging. |
|
|
Channel windows, frame averaging for display, last acquired frames. |
|
|
Beam powers, depth compensation, blanking, power boxes. |
|
|
PMT gain, bandwidth, power and trip state. |
|
|
Shutter transitions. |
|
|
Stage control and the sample coordinate systems. |
|
|
Fast axial actuator: waveform, lag, flyback. |
|
|
Stack and volume definition, slice / volume counters. |
|
|
Computes, caches and optimizes scanner output waveforms. |
|
|
Photostimulation sequences, triggering and monitoring. |
|
|
Online integration ROIs and their output channels. |
|
|
Motion estimation and correction. |
|
|
Tiles and tiled acquisition. |
|
|
Multi-iteration cycle acquisitions. |
|
|
Camera wrappers and their alignment to reference space. |
|
|
Distributes acquired ROI data to consumers. |
|
|
Event hooks for user code. |
|
|
CFG / USR configuration files. |
% every component is also a resource, so the resource store can enumerate them
hComponents = hSI.hResourceStore.filterByClass('dabs.resources.SIComponent');
numInstances
numInstances reports how many hardware instances the component is backed by. A component
with numInstances <= 0 is inert: start returns immediately, property updates are
refused, and functions guarded by componentExecuteFunction do nothing.
This is what makes ScanImage tolerate a partial configuration. If no PMT controller is
configured, hSI.hPmts still exists and can be queried, it simply does not act.
if hSI.hPhotostim.numInstances > 0
hSI.hPhotostim.onDemandStimNow(1);
end
Active state and the acquisition state machine
hSI.acqState is one of 'idle', 'focus', 'grab', 'loop', 'loop_wait'
and 'point'. hSI.active is true whenever the state is not idle and initialization has
completed.
Each component has its own active flag, set by the framework:
hComponent.start(...)asserts the component is not already active, calls the subclass hookcomponentStart, then setsactive. IfcomponentStartthrows, ScanImage aborts before the error is rethrown.hComponent.abort(...)clearsactiveand callscomponentAbort. Errors raised during abort are logged rather than thrown, so an abort always completes.
A component constructed with independentComponent = true is not tied to hSI’s state
and may act while an acquisition is running.
Live update rules
Six constants on every component decide what may change during an acquisition. They are
declared Constant, Hidden in each component class.
Constant |
Meaning |
|---|---|
|
Properties that may be set while the component is active, with no interruption at all. |
|
Properties that may be set live, but only while focusing. |
|
Properties that may not be applied during focus even by the abort-and-restart fallback described below. |
|
Methods callable while the component is active. |
|
Methods callable live while focusing. |
|
Methods that may not be run through the abort-and-restart fallback. |
Property setters call componentUpdateProperty(propName,val) and only proceed if it
returns true; methods guard themselves with componentExecuteFunction(fncName,...).
The fallback behavior is worth knowing about, because it is visible to users: when you set a property that is not in the live-update lists while ScanImage is focusing, ScanImage aborts the focus, applies the change, and restarts focus. During a grab or loop the change is refused with a message on the command window and the old value is kept.
% which properties can be changed live on the ROI manager while focusing?
scanimage.components.RoiManager.PROP_FOCUS_TRUE_LIVE_UPDATE
Note
The constructor checks these lists against the class’s actual properties and methods and warns about stale entries, so a typo shows up at startup rather than silently disabling a live update.
Configuration, machine data and class data
Components mix in one or more of the following, which is where their settings live:
Mixin |
Purpose |
|---|---|
|
Hardware configuration, persisted to the machine data file (MDF) under a heading named
by |
|
Bulk data that does not belong in a text configuration file - calibrations, alignment
tables, cached waveforms - persisted to a |
|
Gives the component a page in the resource configuration editor. Call
|
CFG and USR files are handled separately by hSI.hConfigurationSaver, which saves the observable model properties of all components rather than the hardware configuration.
Property metadata and the Tiff header
Components are most.Model objects. Each declares mdlPropAttributes (built by a local
ziniInitPropAttributes function at the bottom of the class file) describing type, range
and dimensionality for its properties; the setters run these through
validatePropArg. mdlHeaderExcludeProps lists properties that should not be written
into acquisition metadata.
The header written into Tiff files is produced by hSI.getHeaderString(); ROI definitions
are serialized separately by hSI.getRoiDataString(). Extra properties can be added with
hSI.mdlCustomProps = {'hRoiManager.scanZoomFactor'}; % model properties
hSI.extCustomProps = {...}; % properties outside the model
See also
Output Files for the layout of the resulting file.
Writing code against a component
Components are handle objects with observable properties, so the natural way to react to a change is a listener:
hL = most.ErrorHandler.addCatchingListener(hSI.hStackManager,'slicesDone','PostSet', ...
@(src,evt)fprintf('slice %d\n',hSI.hStackManager.slicesDone));
most.ErrorHandler.addCatchingListener is used throughout ScanImage in preference to
addlistener because it logs and contains errors raised in the callback instead of letting
them break the acquisition.
For acquisition lifecycle hooks - start, done, abort, per-frame - use user functions rather than listeners; they are designed for it and are saved with the configuration.