Skip to content

Mover APIs

LabExT's Mover offers several possibilities to extend functionalities by user-defined code. This guide gives practical hints to extend the Mover.

Stages

To support another Stage, a new Stage implementation must be created. Currently, the following stages are supported in LabExT-Core. New stages can be included by an addon path or permanently supported in Core by creating a pull request.

Each new stage must inherit from the abstract interface Stage in order to be included by LabExT:

from LabExT.Movement.Stage import Stage


class MyNewStage(Stage):
    pass

The Stage interface defines all the methods and properties that each new implementation must define to work correctly. Below we provide notes on the methods and properties to implement:

Bases: ABC

Abstract Interface for Stages in LabExT. Inherit from this class to support new stages.

Attributes:

Name Type Description
driver_loaded bool

Indicates whether the drivers for the stage are loaded.

driver_specifiable bool

Indicates whether the user specifies the drivers, for example, by setting a path where the drivers are stored.

description str

Optional description of the stage for the GUI

Source code in LabExT/Movement/Stage.py
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
class Stage(ABC):
    """
    Abstract Interface for Stages in LabExT. 
    Inherit from this class to support new stages.

    Attributes:
        driver_loaded:          Indicates whether the drivers for the stage are loaded.
        driver_specifiable:     Indicates whether the user specifies the drivers,
                                for example, by setting a path where the drivers are stored.
        description:            Optional description of the stage for the GUI
    """
    driver_loaded: bool = False
    driver_specifiable: bool = False
    description: str = ""

    _logger = logging.getLogger()

    @classmethod
    def find_available_stages(cls) -> List[Type[Stage]]:
        """
        Returns a list of stage objects. Each object represents a found stage.
        Note: The stage is not yet connected.
        """
        try:
            return [cls(address) for address in cls.find_stage_addresses()]
        except StageError as err:
            cls._logger.error(
                f"Failed to find available stages for {cls.__name__}: {err}")
            return []

    @classmethod
    def find_stage_addresses(cls) -> list:
        """
        Returns a list of stage locators.
        """
        return []

    @classmethod
    def load_driver(cls, parent=None) -> bool:
        """
        Stage specific method to load any missing drivers.

        Returns True, if successfully loaded and False otherwise.

        Needs to be overwritten.
        """
        raise NotImplementedError

    @abstractmethod
    def __init__(self, address: Any):
        """
        Constructs a new Stage.

        Parameters
        ---------
        address: Any
            Address of Stage

        Attributes
        ----------
        address: Any
            Address of Stage
        connected: bool = False
            Indicates whether the there exists a active connection to the stage.
        """
        self.address: Any = address
        self.connected: bool = False

    def __del__(self):
        if self.connected:
            self.disconnect()

    @abstractmethod
    def __str__(self) -> str:
        """
        Returns the stage in string representation.
        """
        pass

    @property
    def address_string(self) -> str:
        """
        Returns the address of the stage as string.
        Example: 'usb:id:123456789'
        """
        raise NotImplementedError

    @property
    def identifier(self) -> str:
        """
        Returns a identifier for given stage.

        The identifier is used to uniquely distinguish two stages of the same type.
        In most cases the identifier is equal to the address of the stage. 
        """
        raise NotImplementedError

    @property
    def is_stopped(self) -> bool:
        """
        Indicates whether the stage is not moving, i.e. has come to a stop.
        """
        pass

    @abstractmethod
    def connect(self) -> bool:
        """
        Creates a connection to the Stage.

        Returns
        -------
        bool
            Indicates whether the connection was successful.
        """
        pass

    @abstractmethod
    def disconnect(self) -> bool:
        """
        Closes the current connection with the Stage.

        Returns
        -------
        bool
            Indicates whether the connection was successfully closed.
        """
        pass

    @abstractmethod
    def set_speed_xy(self, umps: float) -> None:
        """
        Sets the speed in X and Y direction

        Parameters
        ----------
        umps: float
            Speed in micrometers per second (um/s)
        """
        pass

    @abstractmethod
    def set_speed_z(self, umps: float) -> None:
        """
        Sets the speed in Z direction

        Parameters
        ----------
        umps: float
            Speed in micrometers per second (um/s)
        """
        pass

    @abstractmethod
    def get_speed_xy(self) -> float:
        """
        Returns the current set speed in X and Y direction.

        Returns
        -------
        float
            Speed in micrometers per second (um/s)
        """
        pass

    @abstractmethod
    def get_speed_z(self) -> float:
        """
        Returns the current set speed in Z direction.

        Returns
        -------
        float
            Speed in micrometers per second (um/s)
        """
        pass

    @abstractmethod
    def set_acceleration_xy(self, umps2: float) -> None:
        """
        Sets the acceleration in X and Y direction

        Parameters
        ----------
        umps2: float
            Acceleration in micrometers per square second (um/s^2)
        """
        pass

    @abstractmethod
    def get_acceleration_xy(self) -> float:
        """
        Returns the current set acceleration in X and Y direction.

        Returns
        -------
        float
            Acceleration in micrometers per square second (um/s^2)
        """
        pass

    @abstractmethod
    def get_status(self) -> Tuple[Any, Any, Any]:
        """
        Returns the status of the stage in all axes.

        Returns `None` if the stage does not respond.
        The semantics of the status codes may differ from stage to stage; they do not have to follow a uniform format.

        Example: If all three axes move, a stage could return `(STEPPING, STEPPING, STEPPING)`.

        Returns
        -------
        tuple
            A triple with the status of all three axes.
        """
        pass

    @abstractmethod
    def get_position(self) -> List[float]:
        """
        Returns the position of the stage in X,Y and Z.

        Returns
        -------
        list
            List of three floats representing a 3D point of the position of the stage.
        """
        pass

    @abstractmethod
    def move_relative(
        self,
        x: float = 0,
        y: float = 0,
        z: float = 0,
        wait_for_stopping: bool = True
    ) -> None:
        """
        Orders the stage to move relative to the current position.

        Parameters
        ----------
        x: float = 0
            Relative offset in X direction.
        y: float = 0
            Relative offset in Y direction.
        z: float = 0
            Relative offset in Z direction.
        wait_for_stopping: bool = True
            If true, the method's call is blocked until the stage has reached the target position,
            i.e., all axes are stopped.
        """
        pass

    @abstractmethod
    def move_absolute(
        self,
        x: float = None,
        y: float = None,
        z: float = None,
        wait_for_stopping: bool = True
    ) -> None:
        """
        Orders the stage to move absolute to a given position.

        If a axis of the target coordinate is `None`,
        the stage keeps the current position of this axis constant.

        Parameters
        ----------
        x: float = None
            Absolute position in X direction
        y: float = None
            Absolute position in Y direction
        z: float = None
            Absolute position in Z direction
        wait_for_stopping: bool = True
            If true, the method's call is blocked until the stage has reached the target position,
            i.e., all axes are stopped.
        """
        pass

address_string: str property

Returns the address of the stage as string. Example: 'usb:id:123456789'

identifier: str property

Returns a identifier for given stage.

The identifier is used to uniquely distinguish two stages of the same type. In most cases the identifier is equal to the address of the stage.

is_stopped: bool property

Indicates whether the stage is not moving, i.e. has come to a stop.

__init__(address) abstractmethod

Constructs a new Stage.

Parameters

address: Any Address of Stage

Attributes

address: Any Address of Stage connected: bool = False Indicates whether the there exists a active connection to the stage.

Source code in LabExT/Movement/Stage.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
@abstractmethod
def __init__(self, address: Any):
    """
    Constructs a new Stage.

    Parameters
    ---------
    address: Any
        Address of Stage

    Attributes
    ----------
    address: Any
        Address of Stage
    connected: bool = False
        Indicates whether the there exists a active connection to the stage.
    """
    self.address: Any = address
    self.connected: bool = False

__str__() abstractmethod

Returns the stage in string representation.

Source code in LabExT/Movement/Stage.py
126
127
128
129
130
131
@abstractmethod
def __str__(self) -> str:
    """
    Returns the stage in string representation.
    """
    pass

connect() abstractmethod

Creates a connection to the Stage.

Returns

bool Indicates whether the connection was successful.

Source code in LabExT/Movement/Stage.py
158
159
160
161
162
163
164
165
166
167
168
@abstractmethod
def connect(self) -> bool:
    """
    Creates a connection to the Stage.

    Returns
    -------
    bool
        Indicates whether the connection was successful.
    """
    pass

disconnect() abstractmethod

Closes the current connection with the Stage.

Returns

bool Indicates whether the connection was successfully closed.

Source code in LabExT/Movement/Stage.py
170
171
172
173
174
175
176
177
178
179
180
@abstractmethod
def disconnect(self) -> bool:
    """
    Closes the current connection with the Stage.

    Returns
    -------
    bool
        Indicates whether the connection was successfully closed.
    """
    pass

find_available_stages() classmethod

Returns a list of stage objects. Each object represents a found stage. Note: The stage is not yet connected.

Source code in LabExT/Movement/Stage.py
71
72
73
74
75
76
77
78
79
80
81
82
@classmethod
def find_available_stages(cls) -> List[Type[Stage]]:
    """
    Returns a list of stage objects. Each object represents a found stage.
    Note: The stage is not yet connected.
    """
    try:
        return [cls(address) for address in cls.find_stage_addresses()]
    except StageError as err:
        cls._logger.error(
            f"Failed to find available stages for {cls.__name__}: {err}")
        return []

find_stage_addresses() classmethod

Returns a list of stage locators.

Source code in LabExT/Movement/Stage.py
84
85
86
87
88
89
@classmethod
def find_stage_addresses(cls) -> list:
    """
    Returns a list of stage locators.
    """
    return []

get_acceleration_xy() abstractmethod

Returns the current set acceleration in X and Y direction.

Returns

float Acceleration in micrometers per square second (um/s^2)

Source code in LabExT/Movement/Stage.py
242
243
244
245
246
247
248
249
250
251
252
@abstractmethod
def get_acceleration_xy(self) -> float:
    """
    Returns the current set acceleration in X and Y direction.

    Returns
    -------
    float
        Acceleration in micrometers per square second (um/s^2)
    """
    pass

get_position() abstractmethod

Returns the position of the stage in X,Y and Z.

Returns

list List of three floats representing a 3D point of the position of the stage.

Source code in LabExT/Movement/Stage.py
271
272
273
274
275
276
277
278
279
280
281
@abstractmethod
def get_position(self) -> List[float]:
    """
    Returns the position of the stage in X,Y and Z.

    Returns
    -------
    list
        List of three floats representing a 3D point of the position of the stage.
    """
    pass

get_speed_xy() abstractmethod

Returns the current set speed in X and Y direction.

Returns

float Speed in micrometers per second (um/s)

Source code in LabExT/Movement/Stage.py
206
207
208
209
210
211
212
213
214
215
216
@abstractmethod
def get_speed_xy(self) -> float:
    """
    Returns the current set speed in X and Y direction.

    Returns
    -------
    float
        Speed in micrometers per second (um/s)
    """
    pass

get_speed_z() abstractmethod

Returns the current set speed in Z direction.

Returns

float Speed in micrometers per second (um/s)

Source code in LabExT/Movement/Stage.py
218
219
220
221
222
223
224
225
226
227
228
@abstractmethod
def get_speed_z(self) -> float:
    """
    Returns the current set speed in Z direction.

    Returns
    -------
    float
        Speed in micrometers per second (um/s)
    """
    pass

get_status() abstractmethod

Returns the status of the stage in all axes.

Returns None if the stage does not respond. The semantics of the status codes may differ from stage to stage; they do not have to follow a uniform format.

Example: If all three axes move, a stage could return (STEPPING, STEPPING, STEPPING).

Returns

tuple A triple with the status of all three axes.

Source code in LabExT/Movement/Stage.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
@abstractmethod
def get_status(self) -> Tuple[Any, Any, Any]:
    """
    Returns the status of the stage in all axes.

    Returns `None` if the stage does not respond.
    The semantics of the status codes may differ from stage to stage; they do not have to follow a uniform format.

    Example: If all three axes move, a stage could return `(STEPPING, STEPPING, STEPPING)`.

    Returns
    -------
    tuple
        A triple with the status of all three axes.
    """
    pass

load_driver(parent=None) classmethod

Stage specific method to load any missing drivers.

Returns True, if successfully loaded and False otherwise.

Needs to be overwritten.

Source code in LabExT/Movement/Stage.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@classmethod
def load_driver(cls, parent=None) -> bool:
    """
    Stage specific method to load any missing drivers.

    Returns True, if successfully loaded and False otherwise.

    Needs to be overwritten.
    """
    raise NotImplementedError

move_absolute(x=None, y=None, z=None, wait_for_stopping=True) abstractmethod

Orders the stage to move absolute to a given position.

If a axis of the target coordinate is None, the stage keeps the current position of this axis constant.

Parameters

x: float = None Absolute position in X direction y: float = None Absolute position in Y direction z: float = None Absolute position in Z direction wait_for_stopping: bool = True If true, the method's call is blocked until the stage has reached the target position, i.e., all axes are stopped.

Source code in LabExT/Movement/Stage.py
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
@abstractmethod
def move_absolute(
    self,
    x: float = None,
    y: float = None,
    z: float = None,
    wait_for_stopping: bool = True
) -> None:
    """
    Orders the stage to move absolute to a given position.

    If a axis of the target coordinate is `None`,
    the stage keeps the current position of this axis constant.

    Parameters
    ----------
    x: float = None
        Absolute position in X direction
    y: float = None
        Absolute position in Y direction
    z: float = None
        Absolute position in Z direction
    wait_for_stopping: bool = True
        If true, the method's call is blocked until the stage has reached the target position,
        i.e., all axes are stopped.
    """
    pass

move_relative(x=0, y=0, z=0, wait_for_stopping=True) abstractmethod

Orders the stage to move relative to the current position.

Parameters

x: float = 0 Relative offset in X direction. y: float = 0 Relative offset in Y direction. z: float = 0 Relative offset in Z direction. wait_for_stopping: bool = True If true, the method's call is blocked until the stage has reached the target position, i.e., all axes are stopped.

Source code in LabExT/Movement/Stage.py
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
@abstractmethod
def move_relative(
    self,
    x: float = 0,
    y: float = 0,
    z: float = 0,
    wait_for_stopping: bool = True
) -> None:
    """
    Orders the stage to move relative to the current position.

    Parameters
    ----------
    x: float = 0
        Relative offset in X direction.
    y: float = 0
        Relative offset in Y direction.
    z: float = 0
        Relative offset in Z direction.
    wait_for_stopping: bool = True
        If true, the method's call is blocked until the stage has reached the target position,
        i.e., all axes are stopped.
    """
    pass

set_acceleration_xy(umps2) abstractmethod

Sets the acceleration in X and Y direction

Parameters

umps2: float Acceleration in micrometers per square second (um/s^2)

Source code in LabExT/Movement/Stage.py
230
231
232
233
234
235
236
237
238
239
240
@abstractmethod
def set_acceleration_xy(self, umps2: float) -> None:
    """
    Sets the acceleration in X and Y direction

    Parameters
    ----------
    umps2: float
        Acceleration in micrometers per square second (um/s^2)
    """
    pass

set_speed_xy(umps) abstractmethod

Sets the speed in X and Y direction

Parameters

umps: float Speed in micrometers per second (um/s)

Source code in LabExT/Movement/Stage.py
182
183
184
185
186
187
188
189
190
191
192
@abstractmethod
def set_speed_xy(self, umps: float) -> None:
    """
    Sets the speed in X and Y direction

    Parameters
    ----------
    umps: float
        Speed in micrometers per second (um/s)
    """
    pass

set_speed_z(umps) abstractmethod

Sets the speed in Z direction

Parameters

umps: float Speed in micrometers per second (um/s)

Source code in LabExT/Movement/Stage.py
194
195
196
197
198
199
200
201
202
203
204
@abstractmethod
def set_speed_z(self, umps: float) -> None:
    """
    Sets the speed in Z direction

    Parameters
    ----------
    umps: float
        Speed in micrometers per second (um/s)
    """
    pass

Stage Polygons

TODO

Path Planning Algorithms

The Mover APIs offer the possibility to extend LabExT with user-defined path planning. For this purpose, an interface PathPlanning was defined, from which a concrete new implementation must inherit:

from LabExT.Movement.PathPlanning import PathPlanning


class MyPathPlanning(PathPlanning):
    pass

Below we provide notes on the methods and properties to implement:

Bases: ABC

Abstract base class for path planning.

Source code in LabExT/Movement/PathPlanning.py
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
class PathPlanning(ABC):
    """
    Abstract base class for path planning.
    """

    def __init__(self) -> None:
        self.logger = logging.getLogger(self.__class__.__name__)
        super().__init__()

    @abstractmethod
    def set_stage_target(
        self,
        calibration: Type["Calibration"],
        target: Type[ChipCoordinate]
    ) -> None:
        """
        Sets a new traget for given calibration.

        Parameters
        ----------
        calibration: Calibration
            Instance of a calibration for which a target cooridnate is to be set.
        target: ChipCoordinate
            3D coordinate in the chip system as a target for the passed calibration.
        """
        pass

    @abstractmethod
    def trajectory(self) -> Generator[WaypointCommand, Any, Any]:
        """
        Generates the next waypoint of the path planner.

        The generator must generate waypoint commands.
        These are (named) tuples consisting of the calibration for which the command is meant,
        the calibration coordinate, and a `wait_for_stopping` specifying
        whether the stage should wait until this waypoint command has been executed.
        """
        pass

set_stage_target(calibration, target) abstractmethod

Sets a new traget for given calibration.

Parameters

calibration: Calibration Instance of a calibration for which a target cooridnate is to be set. target: ChipCoordinate 3D coordinate in the chip system as a target for the passed calibration.

Source code in LabExT/Movement/PathPlanning.py
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
@abstractmethod
def set_stage_target(
    self,
    calibration: Type["Calibration"],
    target: Type[ChipCoordinate]
) -> None:
    """
    Sets a new traget for given calibration.

    Parameters
    ----------
    calibration: Calibration
        Instance of a calibration for which a target cooridnate is to be set.
    target: ChipCoordinate
        3D coordinate in the chip system as a target for the passed calibration.
    """
    pass

trajectory() abstractmethod

Generates the next waypoint of the path planner.

The generator must generate waypoint commands. These are (named) tuples consisting of the calibration for which the command is meant, the calibration coordinate, and a wait_for_stopping specifying whether the stage should wait until this waypoint command has been executed.

Source code in LabExT/Movement/PathPlanning.py
290
291
292
293
294
295
296
297
298
299
300
@abstractmethod
def trajectory(self) -> Generator[WaypointCommand, Any, Any]:
    """
    Generates the next waypoint of the path planner.

    The generator must generate waypoint commands.
    These are (named) tuples consisting of the calibration for which the command is meant,
    the calibration coordinate, and a `wait_for_stopping` specifying
    whether the stage should wait until this waypoint command has been executed.
    """
    pass