-
Notifications
You must be signed in to change notification settings - Fork 6
Modules
This page will discuss what a module is, how it is constructed and important concepts.
It will also outline the necessary components for each type of module (abstractly defined in +Modules/ of the repo with their implementations stored in the Modules/+*):
- Prefs
- Settings
- Module Singleton Behavior
- Common Methods, Properties, and Events (e.g. superclass hierarchy)
- Specific Methods and Properties (of all types enumerated above)
Individual module pages are meant to be up to date with the dev branch.
More information on the implementation of prefs classes can be found at the Prefs Wiki. Here we will discuss how you declare a property as a pref.
There are three special properties that CommandCenter looks for:
-
prefs: The set of properties that get saved between sessions (e.g. CommandCenter, MATLAB, or computer restarting). By default, these are the settings as well (ifshow_prefsisn't defined). -
show_prefs(soon to beshow_settings): see settings for more details. For the GUI settings, this completely overridesprefs. It will instruct CommandCenter to display the listed properties as settings in the GUI. It can include any properties (they don't have to be prefs). It has no impact on what gets saved or not to persistent memory. -
readonly[now obsolete]: Instructs CommandCenter to disable the editing of these settings. Instead you should use thereadonlyproperty of the class-based pref.
Prefs are stored to persistent memory when the module gets destroyed.
They can be reloaded into the object by calling the [...] = obj.loadPrefs(...) method (see below for details) of the module which is usually done in the constructor.
For example, a class that has the following properties blocks would be defining two prefs: "my_explicit_double" and "my_implicit_double" (see prefs as to why SetObservable and GetObservable flags are set):
properties(SetObservable,GetObservable)
my_explicit_double = Prefs.Double(5,'min',0,'max',13);
my_implicit_double = 3;
end
properties
prefs = {'my_explicit_double','my_implicit_double'};
endTo make sure these get loaded back to the object upon instantiation, the constructor may look something like this (see module singleton behavior as to why Access=private for constructors):
methods(Access=private)
function obj = my_module()
obj.loadPrefs();
end
endTo finish an example of what behavior this will result in, imagine the following block of code (specific call syntax is explained in module singleton behavior):
mod = my_module.singleton();
fprintf('%i, %i\n',mod.my_explicit_double, mod.my_implicit_double);
mod.my_explicit_double = 10;
mod.my_implicit_double = -2301;
delete(mod); clear('mod'); % Even close MATLAB
mod = my_module.singleton();
fprintf('%i, %i\n',mod.my_explicit_double, mod.my_implicit_double);This would produce the following output:
>> 5, 3
>> 10, -2301The data types that can be saved are generally the fundamental MATLAB data types. There are a few exceptions such as other modules (again, see the prefs page).
Settings are the set of properties that a module would like to be displayed to the user in the GUI. They appear under the default settings for that type of module in that module's panel. In the below image, the first setting defined by the "Camera" module is "binning". A faint white line separates default and custom module settings (black arrow).
Typically:
- They are editable or readonly
- Any property that is implicitly or explicitly supported as a pref.
- By default they are all properties that are prefs. This makes sense because
prefsare the dynamic (not hard-coded) part of a module. This set of properties is extended to include any property defined as a class-based pref. - If
show_prefsis defined, they are exactly that set of properties.
This is the description of the default behavior for the module's obj.get_settings() method. See common methods for more details. However, this can obviously be overloaded if desired (although I think it is flexible enough with properties that it wouldn't make sense to do so).
The most important thing to understand is that this behavior enforces calling the constructor of a module through that module's instance method (see below for details).
The singleton class is the first class inherited by any module. It has a fairly straightforward job: ensure that instances that belong to the "same" physical entity will only generate a single instance. This is important to avoid a situation where two instances are attempting to represent the state of a single piece of hardware. This could result in one of the instances updating it, and the other not knowing, resulting in the other having an outdated picture of the world. For example (assuming the laser starts off):
my_laser = Sources.my_laser.instance();
my_laser2 = Sources.my_laser.instance();
fprintf('Source On: %i, %i\n', my_laser.source_on, my_laser2.source_on);
my_laser.on;
fprintf('Source On: %i, %i\n', my_laser.source_on, my_laser2.source_on);The singleton class will make the following output:
>> Source On: 0, 0
>> Source On: 1, 1whereas a non-singleton implementation would result in two separate instances:
>> Source On: 0, 0
>> Source On: 1, 0 % my_laser2 is wrong!The superclass hierarchy is as follows:
handle handle
│ │
Base.Singleton Base.pref_handler
└─────────┬───────┘
Base.Module
│
Module.[TYPE]
│
[TYPE].[MODULE NAME]
handle • Base.Singleton • Base.pref_handler • Base.Module • Modules.*
Only methods/properties accessible by the module are discussed (public/protected). The prefix is simply to show you where they are defined. From the perspective of the module, these are all inherited (with an exception to constructors and destructors).
-
Base.Singleton.singleton_id: Intended to be used by a module'sinstancemethod to determine if a new instance should be created. This is currently only useful for Driver modules since they can accept input (one class can represent more than one physical piece of equipment identified by an input parameter like IP address, for example). -
Base.Module.logger: This provides access to a logger instance. If CommandCenter is loaded, it will have been set by CommandCenter's opening function to be the logger the rest of CommandCenter is using (Logger.m). If CommandCenter is not open, it resorts to its default value of the console logger. -
Base.Module.modules_package(Abstract, Constant, Hidden): The char array representation of the package name for that type of module. For example, managers use this to know where to begin looking for modules of that type when you click on the associated menu in CommandCenter. -
Base.pref_handler.pref_set_try: Due to the implementation of prefs, errors when setting prefs are "downgraded" to warnings (because all of this is in a listener callback). Warnings do not halt execution of the program which can be problematic in some cases. Setting this to true will prevent the raising of the error (e.g. throwing the warnings), and the error can be retrieved inlast_pref_set_errafter attempting to set. -
Base.pref_handler.last_pref_set_err(readonly): The error that occurred on the last attempted set of a pref or empty.
-
Base.Module.update_settings: When this event is triggered (by calling the notify method), CommandCenter performs the calls necessary to reload the settings panel (as if the module has been loaded/made active for the first time).
-
Base.Singleton.instance(Abstract, Static): This method must be defined for each module created because of how MATLAB's mlock method works. The purpose of this method is to store existing instances and checksingleton_idto determine if a new instance needs to be created. Assuming a module calledModule, two exampleinstancemethod implementations are:
function obj = instance(id)
mlock; % Keep all Objects in memory
persistent Objects % Objects persists between function calls
if isempty(Objects)
Objects = Module.empty(1,0);
end
% Cycle through existing instances to see if they satisfy input
for i = 1:numel(Objects)
if isvalid(Objects(i)) && isequal(id, Objects(i).singleton_id)
obj = Objects(i);
return
end
end
% If none of them do, create a new one and store it
obj = Module(id);
obj.singleton_id = id;
Objects(end+1) = obj;
end
function obj = instance()
mlock; % Only to keep the only instance in memory
persistent Object % Object persists between function calls
if isempty(Object) || ~isvalid(Object)
Object = Module();
end
obj = Object;
end-
Base.Singleton.Singleton(constructor): Assert the module is written "safely" and has been properly called.- Ensure called from the
instancemethod. - Ensure privileges of the module's constructor are private or protected.
- Ensure the
instancemethod locked the object in memory.
- Ensure called from the
-
Base.Singleton.delete: This unlocks the class that should have been locked in theinstancemethod. -
Base.Module.Module(constructor): The module constructor does a few things behind the scenes to keep things running as one would expect.- Generates a namespace to save the (prefs)[#prefs].
- Prepares the logger (to console if instantiated without the rest of CommandCenter)
- Attaches listeners to clean up properties that have a module is their value for when that module gets deleted (see module patterns for more explanation).
- Cycles through all properties to re-construct any modules declared as a default. This is necessary because MATLAB only executes the default properties once (see MATLAB help).
- Adds itself to CommandCenter's list of all instantiated modules.
-
Base.Module.delete: Cleans up the module's footprint and saves prefs.- Saves prefs
- Cleans up listeners to module's destruction event for modules assigned to properties.
- Cleans up itself from CommandCenter's list of instantiated modules.
-
datastruct = Base.Module.prefs2struct([datastruct]): Turns all properties that are prefs into fields of a struct. If a struct is given as input, instead of starting with a new struct, these properties are appended as fields in the given struct. -
[warnings] = Base.Module.loadPrefs(['include'|'-exclude']): With no inputs or outputs requested, this will load all prefs into the object's properties. All errors during this process are thrown as warnings. If an output variable is requested, the errors are instead returned in a struct. Their corresponding field is the name of the pref in which the error occurred. If inputs are specified as property names, only those will be the ones that are loaded. If these inputs have a '-' prefix, those properties will be excluded (e.g.obj.loadPrefs('-pref1','-pref2');will excludeobj.pref1andobj.pref2from being loaded; whereasobj.loadPrefs('pref1','pref2');will result in onlyobj.pref1andobj.pref2being loaded). -
tasks = Base.Module.inactive(): This method is called by the manager when the inactivity timer has triggered. It should take the appropriate action and return a char array describing to the user what action took place. -
settings = Base.Module.get_settings(): This is called by the defaultBase.Module.settingsto retrieve the name of properties to be treated as settings. The default behavior is described in settings. -
Base.Module.settings(panelH, pad, margin): The method responsible for building the module's custom settings. The inputs are the handle to the panel to place everything, the padding in pixels that should be used between settings and the margin specified as a 1x2 ([left, right]) double. This should iterate through the sequence of calls outlined in Prefs to build the UI. It can retrieve a property's pref class by callingmp = obj.get_meta_pref('pref1');. -
Base.pref_handler.pref_handler(constructor): Assigns any callbacks declared in class-based prefs (e.g. theirsetmethods) and attaches Post/Pre Set/Get listeners to all of these prefs. -
el = Base.pref_handler.addlistener(...): Overloaded version of handle.addlistener. If called in a way that would have normally returned an event.proplistener, instead it returns a Base.preflistener (intended to be a drop-in replacement). -
prefs = Base.pref_handler.get_class_based_prefs(): Returns a cell array of character arrays representing the name of all properties defined as class-based prefs. -
val = Base.pref_handler.get_meta_pref('name'): Given the name of a property (does not have to be defined as a class-based pref or a pref at all), return the governing class-based pref object. If it is not explicitly defined as a class-based pref, this method will inspect the default type and the current value to choose the appropriate class-based version and return an instance with default values. In the implicit case,pref.auto_generatedwill be set totrue.
This section is intended to cover a few additional patterns that are defined in Base.Module (not including the inherited singleton patterns).
Suppose module A sets one of its property's default value to module B.
As mentioned above, MATLAB only evaluates default properties once (MATLAB help), so module A's constructor will re-instantiate module B if necessary.
Regardless, module A will "re-set" that property to itself to trigger the listener described next.
The part not covered here is what happens when module B is deleted while it is currently assigned to a property in module A.
If the default value of the property in module A was anything related to a module (e.g. B itself, or Base.Module.empty() or Modules.Imaging.empty()) the constructor of A assigns a PostSet listener to that property (hence why in the case of the default value being B, it gets "re-set" after this listener is created).
The PostSet listener then attaches another event listener on the ObjectBeingDestroyed event of the module being assigned to the property (module B in the above example).
The callback to this event simply sets the original property that used to point to B to an empty array ([]).
For example:
module_A = A.instance(); % In the classdef, this has "prop = Base.Module.empty();"
module_B = B.instance();
module_A.prop = module_B;
% This triggers module_A to attach a listener to module_B's ObjectBeingDestroyed event
fprintf('%s\n', class(module_A.prop));
delete(module_B); % This triggers module_B's notification of its ObjectBeingDestroyed event
% This causes module_A to garbage collect its "prop" property
fprintf('%s\n', class(module_A.prop));would result in the following output:
B % The value being a handle to module_B (the instance of class B)
double % The value being []If this is loaded and an active_module in CommandCenter, this will automatically update the GUI showing that B has been dereferenced (due to being deleted).
This is all done from Manager PostSet listeners.
The way class-based prefs work is to basically monopolize the Pre/Post Set/Get listeners such that the "meta" pref (e.g. the class-based pref instance) can be swapped out when the user tries to set or get the associated property. I will refer to these particular listeners as the "proplisteners". Because of this, and MATLAB's limitations on listeners (namely the unspecified order they are executed) requires a novel approach.
This is solved by implementing pref_handler's own concept of Pre/Post Set/Get listeners (referred to as "preflisteners") that are virtually (I should say, intended to be exactly) identical to MATLAB's implementation.
Unfortunately MATLAB's eventlistener objects are all Sealed, so instead everything is attempted to be re-created in Base.preflistener.
The enabled preflisteners get executed (in the order they are created) in the appropriate Pre/Post Set/Get proplistener's callback that has already been setup for the original property in question.
Each type of module may define further abstract components or implement opt-in methods/properties. A general pattern for opt-in methods is to define it in the superclass with a warning indicating the subclass should overload it. Once properly overloaded, the warning definition of the method will never get called, and the subclass definition will.
Whenever I mention a method is called by interacting with CommandCenter or called via the manager, they mean the exact same thing (I am probably just being lazy).
A module method is never called directly from the GUI.
It will always call a manager method that in turn calls the module method in a sandbox (see the sandboxed_function method in managers for details).
-
Modules.Database.autosave(Abstract): This is a flag that can betrueorfalseand is used by the Database Manager to determine if it should be automatically called after an image or experiment are finished.
-
Modules.Database.SaveIm(image_struct, ax, active_module, notes)(Abstract): Method used to save images. Theimage_structcorresponds to a SmartImage.info struct. Theaxcorresponds to the image axis which can be useful to extract current clim settings but is not necessary to extract image data since that is in theimage_struct. Theactive_modulecorresponds to the presently selected module handle in the "Saving" panel on the left of CommandCenter (specifically corresponding to the DBManager'sactive_moduleproperty). This is not required to be the same module that took the image (again, that meta data can be found inimage_struct! Thenotesinput corresponds to the users notes entered in the "Saving" panel. This method is called by DBManager'simSavemethod. -
Modules.Database.SaveExp(image_struct, ax, active_module, notes)(Abstract): Identical explanation toSaveIm(above) with a few differences. The first input is thedatastruct returned by an experiment module'sGetDatamethod. This method is called by DBManager'sexpSavemethod. -
Modules.Database.LoadExp(): This opt-in method should be the inverse ofSaveExpin that it returns adatastruct. For example, if this database module saved a file when executed, this method should prompt the user to choose a file then load the contents and return what originally wasdata(this may require re-building it if it was deconstructed inSaveExp). Called by DBManager'sexpLoadmethod. -
Modules.Database.LoadIm(): Identical explanation toLoadExp, only instead of returning the originaldata, it should return the originalimage_struct. Should be called by DBManager'simLoadmethod. However, this has yet to be transitioned from the hard-coded file loading procedure in CommandCenter "File" menu's "Load Image in CC" option which has a callback defined in CommandCenter.m namedfile_load_image_Callback. This is captured in issue 67.
-
Modules.Experiment.path: The path name (char array) desired when running this experiment. The Experiment manager will check this value upon calling an experiment module'srunmethod (below). If a name is supplied the manager will attempt to set it via the path manager'sselect_pathmethod. The path manager will attempt to do this and prompt the user if necessary to define the path.
-
Modules.Experiment.save_request: The Experiment manager will listen for a loaded experiment to notify about this event. Once notified, the manager will immediately go through the experiment "autosave" saving procedure (DBManager.expSave(true)).
-
Modules.Experiment.run(statusH, managers, ax): When you click the "run" button in CommandCenter's "Experiment" panel, it calls the Experiment manager'srunmethod which in turn calls this method. The inputs:-
statusHis a handle to the status message box's text object. The text displayed to the user can be updated by modifying theStringproperty of the text object followed by a graphics update in MATLAB (one way of doing that is calling drawnow). -
managersis a ManagerContainer object that provides access to all of the managers and figure handles. It is the same object that CommandCenter assigns in the main workspace upon launching. -
axis the axes object that is the child of the data panel in CommandCenter's GUI. If you would like a different type of axes, or multiple axes, it is acceptable to get the parent panel by grabbingax.Parent. Once the handle to the parent has been stored in a variable, you can delete the axes by callingdelete(ax)and work directly on the panel (which is essentially the same as a figure).
-
-
Modules.Experiment.abort(): When the user clicks abort on the status message box, the Experiment manager'sabortmethod is called which in turn calls this method. It is up to the experiment's implementation of this method to decide how it will interpret the user's attempt to abort. Frequently, this method will toggle a property of the experiment which can serve to notify therunmethod that the user clicked "abort". Therunmethod is then responsible for doing what is necessary to abort. Note that regardless of whatrundoes with this information (if any), the experiment manager will not notify listeners of the experiment finishing, thus will not runautosavesaving modules. See the Experiment manager for more info. -
Modules.Experiment.GetData(stageManager, imagingManager): This is called when the database module attempts to save an experiment. It is passed the stage manager and the imaging manger (however, this is legacy and all meta-data should be extracted at the time the experiment was run in therunmethod (see issue 68!). -
Modules.Experiment.analyze(data)(Static): This serves as the entry method to an experiment's overloadedanalyze(data). It is written in an opt-in style and looks for theanalyze(data)method described in thedatainput. This can be a bit confusing!- User has a
datastruct that is the result of the DBManager'sExpSavemethod. This can be attained by database modules that simply write that struct to file, for example. - That
datastruct will have a field that was added by the database manager calledorigin. This field is the string name of the experiment module where it came from. - The user can call
Modules.Experiment.analyze(data)to analyze the data. Obviously if the module that the data came from has not implemented its ownanalyzemethod, this will error appropriately.
- This "entry" method takes care of preparing the input to a module's
analyzemethod by extracting the data returned by that experiment module'sGetDatamethod which is stored in thedatafield ofdata(in the database manager).
- User has a
-
[my_experiment_module].analyze(data)(Static): I explicitly specify this because this isn't a traditional overloaded method scenario. The previous static methodModules.Experiment.analyze(data)will call this static method. The input to this static method is exactly thedatastruct that is returned byGetData. It should do what ever is necessary to analyze the data and return whatever it needs to. It is intentionally very open-ended, but for example: it could perfrom a fit and return that fit or it could open its own GUI to interact with the dataset. -
Modules.Experiment.new(experiment_name)(Static): This method takes the name of an experiment and auto-generates the module (similar to how declaring a new class in MATLAB will auto-fill part of the file). The template for the new files and file contents is in +Modules/*.template.
-
Modules.Imaging.calibration(um/#): This is currently set and used by the Imaging manager to convert the native imaging-friendly units of pixels or voltage to a distance in microns. The imaging module itself doesn't have to consider this at all in the module; everything there should be its native units. Currently a scalar double is all that is supported. Future versions will hopefully address these issues. -
Modules.Imaging.uses_stage: This instructs SmartImages to subtract out the specified stage when calculating positions. It is especially useful for confocal systems where the imaging device might also be considered a stage. When only the confocal imaging system and the associateduses_stagemodule is loaded, this effectively allows you to click around and move to the absolute location you want rather than relative motion. -
Modules.Imaging.path: The path name (char array) desired when taking an image (very similar toModules.Experiment.path). The Imaging manager will check this value upon calling an imaging module'ssnapandstartVideomethods (below). If a name is supplied the manager will attempt to set it via the path manager'sselect_pathmethod. The path manager will attempt to do this and prompt the user if necessary to define the path. -
Modules.Imaging.ROI(Abstract, SetObservable): A 2x2 double array ([xMin, xMax; yMin, yMax]) specifying the current region-of-interest in the native units of the imaging device (e.g. pixels, voltage, etc.) This is what ends up getting set in the ROI region of the imaging settings panel. CommandCenter takes care of translating units (see issue 69 for a proposed change). It contains the SetObservable flag so that the imaging manager can keep the GUI up to date when/if this is changed elsewhere in MATLAB. -
Modules.Imaging.resolution(Abstract, SetObservable): A 1x2 double array ([x, y]) specifying the size of the x and y axes of the image. The imaging module uses this to pre-allocate memory before calling thesnapmethod. It contains the SetObservable flag so that the imaging manager can keep the GUI up to date when/if this is changed elsewhere in MATLAB. -
Modules.Imaging.continuous(Abstract, SetObservable): This should be set totruewhen the imaging device is taking a live, continuous acquisition, andfalseotherwise. The imaging manager watches this property to determine the action and text of the "Continuous"/"Stop" button in the settings panel. -
Modules.Imaging.maxROI(Abstract): This is only used as a convenience for the user. When the user clicks the "O" button next to the ROI table in the settings panel, it resets the ROI to the value here. As such, it has the same format asROI(defined above).
-
Modules.Imaging.Imaging/delete(Constructor/Destructor): Because imaging modules define thecalibrationproperty but don't define any prefs of their own, the module constructor/destructor play a role to handle this property as if it were a pref. This is also addressed in issue 69. -
metric = Modules.Imaging.focus(ax,stageHandle): This method is responsible for auto-focussing. It is called when the user clicks the "Auto Focus" button in the settings panel. The first argument is a handle to axes such that the method can keep the user informed on what is happening. The second argument is a handle to the active stage module (from the stage manager'sactive_moduleproperty) that will be used to focus. The method should return a metric which is optionally returned when the ImagingManager'sautofocusmethod is called. -
Modules.Imaging.snap(imHandle): This method is responsible for acquiring the image and is called when the "Snap" button in the settings panel is pressed. The image should be saved to theCDataproperty of imHandle (a MATLAB image object). For modules that raster (like confocal imagers) it is ideal if it is continusously updated with periodic calls to flush the graphics queue (like drawnow). -
Modules.imaging.startVideo(imHandle): Similar tosnap, but should keep going! The ideal technique is to have this method setup a timer object that will keep updating untilstopVideois called. However, a loop conditional on a stop flag can also work. This should also toggle thecontinuousproperty totrue. Settingcontinuoustotruewill also result in the button changing from "Continuous" to "Stop" (which also switches the callback as such). -
Modules.imaging.stopVideo(): This should stop whatever acquisition loop is taking place and once finished, set thecontinuousproperty tofalse. This will result in the button getting restored to "Continuous".
Sources have a slightly more intimate connection with the CommandCenter GUI than other modules.
There are a couple properties/methods implemented that I will describe here, but not list below.
obj.CC_dropdown gets set by the source manager when a source gets loaded.
It is a struct that contains the handle to the uicontrol object and the index of the position of the module in the String property of the uicontrol.
The constructor attaches a listener to the obj.source_on property that calls its own obj.updateCommandCenter(~,~) method.
This method uses the information in obj.CC_dropdown to color itself green when on and red when off in the dropdown list of modules enabled.
-
Modules.Source.source_on: Boolean representing if the source is hitting the sample.
These four methods outlined below can usually obey the following relationship (handy to understand, but not 100% necessary in practice):
on -> inverse(off)
arm -> inverse(blackout)
-
Modules.Source.on(): Responsible for turning the source on. Ideally, this is done as fast as possible, perhaps by opening a shutter or AOM. If it is also necessary to turn the power on, or some slow action, it should also be performed, but should be conditional on being required (seearm)! This is called afterarmwhen the "On" button in the settings panel is pressed. -
Modules.Source.off(): Prevent the source from hitting the sample as quickly as possible. This usually entails turning off a shutter or AOM rather than turning the power of the source off (seeblackout). This method is the only one called when pressing "Off" in the settings panel. -
Modules.Source.arm(): This should "prepare" a source to be turned on via theonmethod. This method is expected to take longer thanonand can be called as such. It is always called prior toonwhen pressing the "On" button in the settings panel. -
Modules.Source.blackout(): This method should take whatever action is necessary to ensure there is nothing from the source that hits the sample. Typically this means powering off the source entirely, even if takes extra time.
You may notice a few design choices below between what is observable between position and Moving.
Because it is easier for most stages to query their position rather than having them periodically update it, I decided to have the stage manager only care if a stage is moving.
This shifts the responsibility of keeping position up to date from the module to the stage manager which hopefully makes it much easier to write stage modules.
See the stage manager for details, but basically it sets up a timer while continuous is true to periodically poll the stage position.
This design choice also allows the stage module to use a get method nicely for the position property.
-
Modules.Stage.calibration(um/#): This is very similar to imaging module'scalibrationproperty. It is set during calibration with the Imaging manager when an imaging module hasuses_stageset to the name of an active_stage. Definitely a current weakness at the moment because it is not very general. Hopefully issue 69 can also address this. -
Modules.Stage.position(Abstract): This property should provide an up-to-date position as a 1x3 double when queried. It is perfectly acceptable to do this with a get method. -
Modules.Stage.Moving(Abstract, SetObservable): A boolean representing if the stage is currently moving. The stage manager will listen to this property to determine when it should periodically poll thepositionproperty. -
Modules.Stage.xRange(Abstract, Constant): The total range (1x2 double:[min, max]) in the x dimension. -
Modules.Stage.yRange(Abstract, Constant): The total range (1x2 double:[min, max])in the y dimension. -
Modules.Stage.zRange(Abstract, Constant): The total range (1x2 double:[min, max])in the z dimension.
Note, it can be useful to have a way to change what the x, y, and z directions correspond to physically on the stage so that they can all be aligned if there are multiple stages. There is no nice way of doing this yet (see issue 70).
-
Modules.Stage.Stage/delete(Constructor/Destructor): Because stage modules define thecalibrationproperty but don't define any prefs of their own, the module constructor/destructor play a role to handle this property as if it were a pref. This is also addressed in issue 69. -
Modules.Stage.move(x,y,z): Move the stage to the provided coordinate. The module should allow any of the values to be empty arrays and interpret that as ignoring setting that axis. -
Modules.Stage.home(): Reset the stage to a well-known location. Depending on the type of stage, this could be just going to (0,0,0) or running a homing routine to reference a hardware switch in the stage (e.g. a well-known location). -
Modules.Stage.abort(immediate): This should abort any active motion of the stage. Ifimmediateisfalse, this can be done in a controlled manner. In the case whereimmediate=true, this should be done as fast as possible even if it it will need ot be re-homed after. In the settings panel, theimmediate=truescenario can be invoked by pressing the abort button again (it will have turned red). -
Modules.Stage.getCalibratedPosition(): This provides a method to apply the stagecalibrationto itspositionin a more regulated way (e.g. check for wrong size and NaN). Upon an error, instead of raising it in a fatal way, it is logged as an error and no calibration is applied instead.
Nothing! Drivers are meant to be an "anything goes" model. The reason they are still modules in the first place is to take advantage of singleton behavior, prefs, and logging.