6.4.3. How an instrument plugin works with the Dashboard
An instrument plugin (a DAQ_Move_* or DAQ_xDViewer_* class) is never called by the user interface directly. Three
objects talk to each other, and knowing which one lives in which thread explains most of the rules of
Instrument Plugins. The diagrams below show them; each one is followed by its text version.
the control module (
DAQ_MoveorDAQ_Viewer, with its user interface) lives in the GUI thread;the worker (
ActuatorWorkerincontrol_modules/daq_move.py,DetectorWorkerincontrol_modules/daq_viewer.py) and the plugin it creates live in the plugin thread. The worker receives the commands of the control module, calls the methods of the plugin and sends back what the plugin emits;the controller is the object of the driver, created by the plugin in
ini_stageorini_detector. It is used in the plugin thread, and can be shared by several plugins (see Initialization and closing).
The control module and the worker only exchange ThreadCommand objects
through Qt signals: the control module emits command_hardware, received by queue_command of the worker, and the
worker emits status_sig, received by thread_status of the control module. The plugin reaches the control module
with emit_status, which emits status_sig of its worker (see Instrument Plugins).
Fig. 6.15 The three layers and the threads
Text version: the user interface sends its commands to the control module (command_sig). The control module sends a
ThreadCommand to the worker with command_hardware. The worker lives in the plugin thread and calls the methods
of the plugin (move_abs, grab_data…), which use the controller. What the plugin emits (emit_status,
move_done_signal, dte_signal) goes back through the worker and status_sig to the thread_status method of
the control module, which updates the user interface and emits its own signals for the extensions. Do not touch a widget
from the plugin: send a ThreadCommand.
6.4.3.1. Initialization and closing
Fig. 6.16 Initialization of an instrument, with a master and a slave
Text version:
The control module creates its worker. The first module of a Controller ID (the master) creates a thread; a slave needs the thread of its master and refuses to initialize without it.
The worker is moved to that thread, and the signals
command_hardwareandstatus_sigare connected.The
INI_HARDWAREcommand makes the worker create the plugin and callini_stageorini_detector, in the plugin thread. A slave gets the controller of its master as argument; the master creates it.The result (
initialized,info, the controller) comes back as anINI_HARDWAREstatus. The control module stores the controller, emitsinit_signaland colours its init button (green for a master, magenta for a slave). A failed initialization marks the module in error.Closing: the
CLOSEcommand makes the worker call theclosemethod of the plugin. When the master is closed, the workers of its slaves are closed too and the thread is stopped (quitthenwait, up to 5 s before it is terminated with a warning).
6.4.3.2. Moving an actuator
Fig. 6.17 move_abs of a DAQ_Move (with polling, the default)
Text version:
DAQ_Move.move_absdoes nothing when the value equals the current one (withinepsilon): it reports aMOVE_DONEat once. Otherwise it sendsRESET_STOP_MOTIONandMOVE_ABSto the worker.The worker converts the position to the units of the axis (
axis_unit), resetsmove_is_done, callsplugin.move_absand thenplugin.poll_moving.With polling (the default),
poll_movingstarts the timer of the plugin. At each interval (polling_interval_msof the configuration)check_target_reachedcallsget_actuator_value, sends it to the control module (GET_ACTUATOR_VALUEstatus, displayed live), and stops with aRAISE_TIMEOUTstatus when thetimeoutsetting is exceeded.When the target is reached,
move_doneof the plugin emitsmove_done_signal. The worker forwards it once per move as aMOVE_DONEstatus, and the control module displays the position and emits its ownmove_done_signal, which is what extensions wait for.Without polling (
ispollingFalse), no timer runs:poll_movingitself callsmove_donejust aftermove_absreturned, with the target value or, if the plugin has an encoder, the value it reads. A plugin whose move only ends later (a homing, a callback of the driver) callsmove_doneitself when it ends.An exception raised by a plugin method is caught by the worker and sent as an
ERRORstatus: the control module writes it in its status bar and is marked in error until the next status coming from the hardware.
move_rel follows the same path, and move_home calls plugin.move_home, which must call move_done when the
homing is over. stop_motion sets motion_stopped, calls plugin.stop_motion and stops the timer.
6.4.3.3. Grabbing data
Fig. 6.18 grab_data of a DAQ_Viewer
Text version:
The Snap button sends
SINGLEand the Grab buttonGRABto the worker, with the number of data to average.The worker calls
plugin.grab_data. If the plugin setslive_mode_available, it is called once and streams its data untilSTOP_GRAB; otherwise the worker calls it again after each data, waitingwait_timebetween two calls in continuous mode.The plugin emits its data with
dte_signal(aDataToExport, see Emission of data). The worker’sdata_readyaverages them ifNaverageis larger than 1 and the plugin does not average itself (hardware_averaging), then emitsdata_detector_sig.In the GUI thread,
show_datacopies the data, processes them (background, live averaging), sends them to the viewers and emitsgrab_done_signal, which extensions and the saving use.STOP_GRABsetsgrab_stateto False and callsplugin.stop; the worker then sendsgrab_stoppedto the control module, which emitsgrab_status(False).
A thread of the driver (a callback or a wait for the camera, see Data ready?) never touches the control module: it
only emits dte_signal of the plugin, or a Qt signal received in the plugin thread.