Resource Framework

dabs.resources.Resource is the base class of everything dabs knows about. A resource has a unique name, a lifecycle, an error state, and a record of who is using it. The class hierarchy underneath it is small:

Class

Represents

dabs.resources.Resource

Anything nameable and shareable.

dabs.resources.Device

A piece of hardware. Every driver in dabs derives from this.

dabs.resources.DAQ

A data acquisition board. Owns its IO channels.

dabs.resources.IO

A single channel on a DAQ - AO, AI, DO, DI, PFI, CLK.

dabs.resources.SerialPort, dabs.resources.VISA

Communication endpoints.

dabs.resources.SIComponent

A ScanImage component. Components are resources so that they can find each other and be enumerated the same way devices are.

dabs.resources.MicroscopeSystem

Groups the resources belonging to one microscope.

dabs.resources.InvalidResource

Placeholder returned when a name does not resolve, so that configuration code can report a missing device instead of crashing.


Identity and state

Property

Description

name

Immutable char array, unique within the resource store. Assigned in the constructor.

errorMsg

Non-empty when the resource is unusable. A resource in an error state refuses to be reserved.

warnMsg

Non-empty when the resource is usable but something is off.

hResourceStore

The store the resource registered itself with.

Both message properties are SetObservable and AbortSet, so a widget or script can listen for hardware going into and out of an error state:

hMotor = dabs.resources.ResourceStore.filterByNameStatic('Stage');
hL = most.ErrorHandler.addCatchingListener(hMotor,'errorMsg','PostSet', ...
     @(varargin)fprintf('%s: %s\n',hMotor.name,hMotor.errorMsg));

assertNoError() throws if errorMsg is set - use it at the top of a script that depends on the device.


Lifecycle

Method

Description

hResource.reinit()

Reserve the resources this one needs and initialize the hardware.

hResource.deinit()

Release resources and deinitialize the hardware.

hResource.delete()

Remove the resource from the store.

Important

reinit and deinit must not throw. A driver that hits a problem during initialization sets errorMsg instead. This is what lets ScanImage start on a partially working system and tell you which device failed, rather than aborting startup.


Users and reservations

Resources are shared, and dabs tracks that sharing explicitly in two layers.

Users are the long-lived relationships: a scanner declaring that it uses a particular analog output. Registration carries a human-readable description that shows up in the configuration editor when two devices claim the same channel.

Method

Description

hResource.registerUser(hUser,description,allowMultipleUsers)

Declare that hUser uses this resource. Re-registering the same user replaces the previous entry. allowMultipleUsers defaults to false.

hResource.unregisterUser(hUser)

Withdraw the declaration.

[tf,mask] = hResource.isUser(hUser)

Whether a given object is registered.

description = hResource.getUserDescription(hUser)

The description that was registered.

[tf,conflictUsers] = hResource.hasUserConflict()

Whether two users that do not allow sharing have both claimed this resource, and who they are.

hResource.userInfo

Read-only summary of the registered users.

Reservations are the short-lived exclusive claims taken while a resource is actually being driven.

Method

Description

hResource.reserve(hUser)

Take the reservation. Throws when the resource is in an error state, when hUser is not a registered user, or when someone else already holds the reservation - and the message names the current holder.

hResource.unreserve(hUser)

Release it.

hResource.forceUnreserve(hUser)

Release someone else’s reservation. A recovery path, not a normal one.

hResource.reserverInfo

Read-only summary of the current reservation.

[tf,conflicts] = hAO.hasUserConflict();
if tf
    cellfun(@(h)fprintf('conflicting user: %s\n',h.name),conflicts);
end

Warning

forceUnreserve takes a resource away from whatever is currently driving it. Use it only to recover from a driver that failed to release its reservation.


Configuration

Most drivers additionally mix in:

Mixin

Purpose

most.HasMachineDataFile

Persist settings to a heading in the machine data file.

dabs.resources.configuration.HasConfigPage

Provide a page in the resource configuration editor. Requires a ConfigPageClass property and a static getDescriptiveNames(). Call hResource.showConfig().

dabs.resources.widget.HasWidget

Provide a widget for the widget bar. Requires a WidgetClass property.

Resources are instantiated from the machine data file:

hResources = dabs.resources.Resource.instantiateFromMdf(mdfPath);

and the mapping from an MDF heading to a class and instance name is available as

[classname,name,isResource,isConstructable] = ...
    dabs.resources.Resource.mdfHeadingToClassAndName(heading);

Getting a handle in the base workspace

assigninBase() drops the resource into the base workspace as hResource, which is handy while exploring from the command window:

hRS = dabs.resources.ResourceStore();
hRS.filterByName('vDAQ0').assigninBase();