BatchManager

BatchManager is the abstract base class that controls the lifecycle of an acquisition run from the main window’s perspective. Every acquisition in Blackchirp — whether it consists of a single experiment or a timed sequence of experiments — is represented by a concrete BatchManager subclass and submitted to MainWindow::startBatch. The main window connects the batch’s signals to AcquisitionManager and the UI, then triggers the first experiment via HardwareManager. The user-facing workflow for starting experiments and batch sequences is described in Experiment Setup.

BatchManager lives on the main (GUI) thread. Its signals reach AcquisitionManager (which lives on a dedicated background thread) via queued connections. Slots on BatchManager are called from the GUI thread by the main window or by AcquisitionManager’s experimentComplete signal.

The immediate collaborators are:

  • Experiment — each experiment in the batch is a shared pointer that currentExperiment() returns to the caller.

  • AcquisitionManager — drives the hardware acquisition loop for each experiment; its experimentComplete signal is connected to BatchManager::experimentComplete for the duration of the batch.

The two built-in concrete subclasses are BatchSingle (wraps a single experiment; BatchType::SingleExperiment) and BatchSequence (repeats an experiment template on a configurable interval; BatchType::Sequence). The batch type in use is identified by the BatchType enum stored in d_type.

State machine

The iterate-over-experiments loop proceeds as follows:

  1. MainWindow::startBatch connects signals, then calls HardwareManager::initializeExperiment(currentExperiment()) to start the first experiment. BatchManager emits beginExperiment() for subsequent experiments (see step 5).

  2. HardwareManager configures hardware and emits experimentInitialized. The main window calls Experiment::initialize() on success, then invokes AcquisitionManager::beginExperiment(exp) on the AM thread.

  3. AcquisitionManager runs the acquisition loop and, when it ends (normally or via abort), emits its experimentComplete signal.

  4. That signal is connected to BatchManager::experimentComplete (the slot). The slot:

    1. Logs the experiment result via Experiment::d_endLogMessage.

    2. Calls processExperiment() if hardware and software initialization both succeeded.

    3. Evaluates isComplete() and whether the experiment was aborted.

  5. If initialization succeeded, the experiment was not aborted, and isComplete() returns false, the slot calls beginNextExperiment(), which by default emits beginExperiment() immediately. The main window responds by invoking HardwareManager::initializeExperiment(currentExperiment()) for the next experiment (go to step 2).

  6. If the batch is complete, or if the experiment was aborted, or if initialization failed:

    • abort() is called when the experiment was aborted or init failed.

    • writeReport() is called unconditionally.

    • batchComplete(aborted) is emitted, where aborted reflects Experiment::isAborted().

The connection between AcquisitionManager::experimentComplete (signal) and BatchManager::experimentComplete (slot) is the central handoff between the two managers. The signal fires on the AM thread and is delivered to the BM slot on the GUI thread via a queued connection.

Subclassing guide

A new batch type inherits BatchManager and implements the following pure-virtual methods:

currentExperiment()

Return the experiment that is either in progress or about to be acquired. Called by the main window each time beginExperiment() is emitted, and by experimentComplete() to inspect the result. Must never return a null pointer while the batch is active.

isComplete()

Return true when no further experiments remain. Evaluated by experimentComplete() after processExperiment() returns.

abort()

Mark the batch as complete so isComplete() returns true and release any pending timers or queued work. Called when the user clicks the abort button or when hardware initialization fails.

processExperiment()

Perform any post-acquisition analysis or bookkeeping for the most recently completed experiment. Called only when initialization succeeded. May be a no-op.

writeReport()

Write a summary report for the batch (file, log entry, or nothing). Called unconditionally after the last experiment ends, before batchComplete is emitted.

The optional override beginNextExperiment() is available when the default behavior — emit beginExperiment() immediately — is not appropriate. For example, BatchSequence overrides this method to start a QTimer and emit beginExperiment() only after the configured inter-experiment interval has elapsed.

To register a new batch type with the main window, add a BatchType enumerator, construct the concrete subclass in the appropriate MainWindow action handler, and pass it to MainWindow::startBatch.

API Reference

class BatchManager : public QObject

Abstract base class that iterates one or more experiments through AcquisitionManager and signals completion to the main window.

BatchManager coordinates the lifecycle of an acquisition run, whether it consists of a single experiment (BatchSingle) or a timed sequence (BatchSequence). The main window constructs a concrete subclass and passes it to MainWindow::startBatch(), which connects the batch’s signals to AcquisitionManager and the UI, then triggers the first experiment via HardwareManager::initializeExperiment().

BatchManager lives on the main (GUI) thread. Its signals cross into the AcquisitionManager thread via queued connections; its slots are called from the GUI thread by the main window or by AcquisitionManager’s experimentComplete() signal.

Subclass authors must implement the five pure-virtual hooks described below. The optional beginNextExperiment() override is available when the default behavior — emit beginExperiment() immediately — is not appropriate (for example, BatchSequence delays the next experiment by a configurable interval).

See also

AcquisitionManager, BatchSingle, BatchSequence

Subclassed by BatchSequence, BatchSingle

Public Types

enum BatchType

Identifies the concrete batch type in use.

Values:

enumerator SingleExperiment

A single experiment managed by BatchSingle.

enumerator Sequence

A timed multi-experiment sequence managed by BatchSequence.

Public Functions

virtual std::shared_ptr<Experiment> currentExperiment() = 0

Returns the experiment that is in progress or about to be acquired.

Called by the main window inside the beginExperiment() signal handler to pass the experiment to HardwareManager::initializeExperiment(). Also called by experimentComplete() to inspect the result of the last acquisition.

Returns:

Shared pointer to the active experiment. Must never be null while the batch is in progress.

explicit BatchManager(BatchType b)

Constructs the base with the given batch type.

Parameters:

b – The BatchType that identifies the concrete subclass.

virtual ~BatchManager()

Destroys the batch manager.

virtual bool isComplete() = 0

Returns true when no further experiments remain to be acquired.

Evaluated inside experimentComplete() after processExperiment() returns. If true, the batch writes its report and emits batchComplete().

Returns:

true if the batch sequence has finished all configured experiments.

Public Slots

void experimentComplete()

Responds to AcquisitionManager::experimentComplete().

Logs the experiment result, calls processExperiment() if initialization succeeded, then either advances to the next experiment via beginNextExperiment() or ends the batch by calling writeReport() and emitting batchComplete().

The decision tree is:

  • If init succeeded and batch is not complete: call beginNextExperiment().

  • If init succeeded and batch is complete: write report, emit batchComplete(false).

  • If the experiment was aborted or init failed: call abort(), write report, emit batchComplete(true).

Note

This slot and AcquisitionManager::experimentComplete() share the same name but are different entities: the AM signal triggers this BM slot.

virtual void beginNextExperiment()

Advances to the next experiment in the batch.

The default implementation emits beginExperiment() immediately. Subclasses may override to insert a delay or other inter-experiment logic (see BatchSequence).

Note

Called by experimentComplete() when the batch is not yet complete and initialization of the previous experiment succeeded.

virtual void abort() = 0

Aborts the batch unconditionally.

Implementations must mark the batch as complete so that isComplete() returns true and no further experiments are started. They must also ensure that any pending timers or resources are released.

Note

Called by experimentComplete() when the experiment was aborted or hardware initialization failed, and by the main window abort button.

Signals

void statusMessage(QString msg, int timeout = 0)

Requests that a transient status string be displayed in the status bar.

Parameters:
  • msg – The status text.

  • timeout – Display duration in milliseconds; 0 means indefinite.

void logMessage(QString msg, LogHandler::MessageCode code = LogHandler::Normal)

Requests that a message be appended to the application log.

Parameters:
  • msg – The text to log.

  • code – Severity classification; defaults to LogHandler::Normal.

void beginExperiment()

Signals that the next experiment in the batch should begin.

The main window connects this signal to a lambda that calls HardwareManager::initializeExperiment(currentExperiment()). Emitted by beginNextExperiment() (or by an overriding subclass after a delay).

void batchComplete(bool aborted)

Emitted when the batch has finished, either normally or by abort.

Connected to MainWindow::batchComplete(). After this signal, the batch manager is no longer active and will be deleted on the next batch start.

Parameters:

abortedtrue if the last experiment was aborted; false for normal completion.

Protected Functions

virtual void writeReport() = 0

Writes a summary report for the completed batch.

Called by experimentComplete() after the last experiment finishes (either normally or by abort) and before batchComplete() is emitted. Implementations may write a file, log a summary, or take no action.

virtual void processExperiment() = 0

Performs any post-acquisition processing for the most recently completed experiment.

Called by experimentComplete() after a successful initialization/acquisition cycle, before isComplete() is evaluated. Implementations may re-analyze data, update state, or do nothing.

Protected Attributes

BatchType d_type

The concrete batch type set at construction.