The most Framework

most is the foundation layer that both scanimage and dabs are built on. It is not something you would normally program against directly, but its idioms appear in every example in this section - most.ErrorHandler.addCatchingListener, most.idioms.isValidObj, mdlPropAttributes, makeEntry - so it is worth knowing what they do and why ScanImage® prefers them over the plain MATLAB equivalents.


most.Model

most.Model is the model half of ScanImage’s model-view-controller split. Every component is a most.Model. It provides three things.

Property attributes and validation

A subclass declares an mdlPropAttributes struct whose fields name properties and describe their constraints:

Field

Meaning

Classes

Valid classes for the property.

Attributes

Valid attributes, in the sense of validateattributes.

DependsOn

Other properties whose value this one depends on.

By convention the struct is built by a local ziniInitPropAttributes() function at the bottom of the class file. Setters run values through obj.validatePropArg(propname,val), which is why setting a component property to a nonsense value produces a message naming the property rather than a failure deeper in.

DependsOn is what makes dependent properties observable: ScanImage binds listeners so that a change to prop1 fires a notification on the dependent property. Dependencies can reach across objects:

s.DependentProp = struct('DependsOn',{'prop1' 'hSibling.prop2' 'hParent.prop3'});

The order of fields in mdlPropAttributes is honored during initialization and when applying a property set - a property with no constraints can be listed with an empty struct purely to pin its position.

Property sets

Property sets are serialized snapshots of object state. They are what CFG and USR files are made of.

Method

Description

obj.mdlSavePropSet(propSet,fname)

Write a property set to file.

obj.mdlSavePropSetFromList(propList,fname)

Write only the listed properties.

obj.mdlLoadPropSet(fname)

Load and apply.

propSet = obj.mdlLoadPropSetToStruct(fname)

Load without applying.

orig = obj.mdlApplyPropSet(propSet,tfOrder,skipEqualVals)

Apply a set, returning the previous values.

obj.mdlApplyPropSetFromList(propSet,propList,tfOrder)

Apply a subset.

hSI.hConfigurationSaver sits on top of these.

Headers

Method

Description

str = obj.mdlGetHeaderString(subsetType,subsetList,numericPrecision)

Readout of all gettable properties as text.

s = obj.mdlGetHeaderStruct(subsetType,subsetList,numericPrecision)

The same as a struct, which is what the JSON header format serializes.

mdlHeaderExcludeProps on each class lists properties to leave out - device handles, transient state, anything that would be noise in a file header. See Acquisition Metadata.

Submodels

A model may own submodels one level deep. Declaring a property’s Classes as 'most.Model' nests that submodel’s mdlPropAttributes into the parent’s during initialization, so the parent’s property sets and headers cover the child too.


most.HasMachineDataFile

The machine data file (MDF) holds hardware configuration as readable text, grouped into headings. A class that mixes in most.HasMachineDataFile declares:

properties (Constant, Hidden)
    mdfClassName = mfilename('class');
    mdfHeading   = 'Shutter';

    mdfDependsOnClasses;
    mdfDirectProp;
    mdfPropPrefix;

    mdfDefault = defaultMdfSection();
end

and implements loadMdf and saveMdf. Entries are built with a static helper:

most.HasMachineDataFile.makeEntry(name,value,comment,liveUpdate)

The comment becomes the inline comment in the MDF, so it is the user-facing description of the setting - worth writing properly.

Member

Description

obj.safeSetPropFromMdf(propName,mdfVarName)

Read one variable into a property, returning success rather than throwing.

obj.safeWriteVarToHeading(mdfVarName,value)

Write one property back.

obj.deleteAndRemoveMdfHeading()

Remove the object’s heading from the file.

most.HasMachineDataFile.updateMachineDataFile(fullfn)

Migrate an MDF to the current format.

See also

Writing a Device Driver for the full pattern in context.


most.HasClassDataFile

Class data files are .mat files holding data that does not belong in a text configuration file: calibrations, alignment tables, cached waveforms. most.HasMachineDataFile derives from this class, so a device with an MDF heading also has class data available.

Method

Description

obj.ensureClassDataFile(initValStruct,classDataFileName,className)

Create the file with defaults if it does not exist.

val = obj.getClassDataVar(varName,classDataFileName,className)

Read one variable.

obj.setClassDataVar(varName,val,classDataFileName,className)

Write one variable.

obj.backupClassData(className)

Back the file up before overwriting.

Static equivalents - getClassDataVarStatic, setClassDataVarStatic, ensureClassDataFileStatic, deleteClassDataFile, backupClassDataFileStatic - work without an instance, and can optionally search superclasses.

hSI.hCoordinateSystems is the most visible user of this mechanism: the whole coordinate system tree is serialized into one class data file.

See also

Reset Class Data Files for recovering from a corrupted file.


most.ErrorHandler

most.ErrorHandler is why ScanImage stays up when one device misbehaves. It logs errors centrally, keeps a history, and - most importantly for user code - provides listeners that contain their own exceptions.

Method

Description

h = most.ErrorHandler.addCatchingListener(...)

Drop-in replacement for addlistener. An error raised inside the callback is logged and contained instead of propagating into whatever triggered the notification. Use this rather than ``addlistener`` in ScanImage code.

ME = most.ErrorHandler.logAndReportError(ME,message)

Log an error and report it to the user.

ME = most.ErrorHandler.logError(ME,message)

Log without reporting.

most.ErrorHandler.reportError(ME,...) / printError(ME,...)

Report or print an error.

most.ErrorHandler.assert(condition,...)

Assertion that routes through the handler.

most.ErrorHandler.error(...), warn(msg), throw(ME), rethrow(ME), throwAsCaller(ME)

Handler-aware equivalents of the built-ins.

[out{:}] = most.ErrorHandler.tryCatch(fcnHdl,...)

Run a function, containing any error.

[MEs,timestamps] = most.ErrorHandler.errorHistory()

The recorded error history.

most.ErrorHandler.startLogging(logFileName) / stopLogging()

Log errors to a file - useful when chasing an intermittent fault.

most.ErrorHandler.resetHistory(), setHistoryLength(n), setVerbose(tf), setPrettyPrint(tf), setErrorCallback(fcn)

Configure the handler.

% contained listener - an error in the callback will not break the acquisition
hL = most.ErrorHandler.addCatchingListener(hSI.hStackManager,'slicesDone','PostSet', ...
     @(src,evt)myCallback());

% capture a session's errors to a file
most.ErrorHandler.startLogging('C:\logs\si_errors.txt');

Warning

A contained error is still an error. If a callback silently stops doing anything useful, check most.ErrorHandler.errorHistory() before assuming the event stopped firing.


most.idioms

A grab-bag of small helpers. The ones that appear constantly in ScanImage source:

Function

Purpose

most.idioms.isValidObj(h)

True for a handle that is both non-empty and not deleted. Guards two failure modes in one call, which is why it is used in place of isvalid.

most.idioms.safeDeleteObj(h)

Delete a handle if it is valid; no-op otherwise.

most.idioms.warn(msg) / dispError(...)

Warning and error output that matches ScanImage’s console style.

most.idioms.figure(...), axes(...), figureSquare(...)

Figure and axes creation with ScanImage’s defaults applied.

most.idioms.string2Enum(val,enumClass)

Convert a string to an enumeration member - how enum-valued properties accept text.

most.idioms.ifthenelse(cond,a,b)

Inline conditional.

Other subpackages worth knowing about: most.json (used for the JSON Tiff header), most.util (Singleton, Uuid, DelayedEventListener, findAllClasses), most.math, most.fileutil and most.gui.

most.util.DelayedEventListener deserves a mention: it is the debounce used where a property changes far more often than consumers need to react - hSI.hScan2D’s scannersetChanged event and hSI.hMotors’s samplePositionChanged event are both built on it.

% fire at most once every 100 ms, no matter how often the property changes
hL = most.util.DelayedEventListener(0.1,hObj,'someProp','PostSet',@(varargin)myCallback());