Skip to content

controller_base

borealis.controller.controller_base

Created on Thu February 9 14:48:59 2023.

@author: A. Vancraeyenest

Controller(*args, alias='', **kwargs)

Abstract base class for Controller classes.

Defines the public API of any motor controller class. All new controller class must override all public methods and properties.

ABC method for controller initialisation. (derived must override).

Source code in borealis/controller/controller_base.py
25
26
27
28
@abstractmethod
def __init__(self, *args, alias: str = "", **kwargs):
    """ABC method for controller initialisation. (derived must override)."""
    self.alias = alias if alias != "" else "Undefined"

__str__()

Custom str method for Controller class (DO NOT OVERWRITE THIS METHOD).

Source code in borealis/controller/controller_base.py
30
31
32
def __str__(self):
    """Custom __str__ method for Controller class (DO NOT OVERWRITE THIS METHOD)."""
    return f'{self.__class__.__name__}(alias={self.alias})'

get_axis_position(axis_id) abstractmethod

ABC method to retrieve position of a single axis (derived must override).

Parameters:

Name Type Description Default
axis_id str

Axis ID as used by the controller.

required

Returns:

Name Type Description
position float

Current position (dial) of the axis.

Source code in borealis/controller/controller_base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@abstractmethod
def get_axis_position(self, axis_id: str):
    """
    ABC method to retrieve position of a single axis (derived must override).

    Parameters
    ----------
    axis_id : str
        Axis ID as used by the controller.

    Returns
    -------
    position : float
        Current position (dial) of the axis.

    """

is_axis_ready(axis_id) abstractmethod

ABC method to check that a given axis is ready, i.e. idle (derived must override).

Parameters:

Name Type Description Default
axis_id str

Axis ID as used by the controller.

required

Returns:

Type Description
bool

True if axis is idle, False if busy or in error state.

Source code in borealis/controller/controller_base.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@abstractmethod
def is_axis_ready(self, axis_id: str):
    """
    ABC method to check that a given axis is ready, i.e. idle (derived must override).

    Parameters
    ----------
    axis_id : str
        Axis ID as used by the controller.

    Returns
    -------
    bool
        True if axis is idle, False if busy or in error state.

    """

is_limit_switch_activated(axis_id) abstractmethod

ABC method to check if the limit switch is active (derived must override).

Parameters:

Name Type Description Default
axis_id str

Axis ID as used by the controller.

required

Returns:

Type Description
bool

True if limit switch activated, False if not.

Source code in borealis/controller/controller_base.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@abstractmethod
def is_limit_switch_activated(self, axis_id: str):
    """
    ABC method to check if the limit switch is active (derived must override).

    Parameters
    ----------
    axis_id : str
        Axis ID as used by the controller.

    Returns
    -------
    bool
        True if limit switch activated, False if not.
    """
    raise NotImplementedError

log(level, msg, *args, **kwargs)

Log a message with prepending the device's alias in front of the message.

Source code in borealis/controller/controller_base.py
171
172
173
174
175
def log(self, level, msg, *args, **kwargs):
    """Log a message with prepending the device's alias in front of the message."""
    kwargs['stacklevel'] = 2
    # LOGGER.log(level, f'{self.alias}: {msg}', *args, **kwargs)
    LOGGER.log(level, f'{self.alias}: {msg}', *args, **kwargs)

move_axis(axis_id, target=0) abstractmethod

ABC method for moving a single axis (derived must override).

This method should move the axis and wait that the axis had reached the target by calling self.is_in_position(axis_id, target)

Parameters:

Name Type Description Default
axis_id str

Axis ID as used by the controller.

required
target float

Target position. Default to 0.

0

Returns:

Type Description
None
Source code in borealis/controller/controller_base.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@abstractmethod
def move_axis(self, axis_id: str, target: float = 0):
    """
    ABC method for moving a single axis (derived must override).

    This method should move the axis and wait that the axis had reached the target
    by calling self.is_in_position(axis_id, target)

    Parameters
    ----------
    axis_id : str
        Axis ID as used by the controller.
    target : float
        Target position. Default to 0.

    Returns
    -------
    None

    """
    raise NotImplementedError

set_axis_to_zero(axis_id) abstractmethod

ABC method to set the axis position to zero (derived must override).

Parameters:

Name Type Description Default
axis_id str

Axis ID as used by the controller.

required

Returns:

Type Description
None
Source code in borealis/controller/controller_base.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
@abstractmethod
def set_axis_to_zero(self, axis_id: str):
    """
    ABC method to set the axis position to zero (derived must override).

    Parameters
    ----------
    axis_id : str
        Axis ID as used by the controller.

    Returns
    -------
    None

    """
    raise NotImplementedError

wait_motion_end(axis_id, target, timeout=60)

Check that the axis has reached its target position.

Parameters:

Name Type Description Default
axis_id str

axis ID as registered in the controller object

required
target float

Position (dial) the axis must reach.

required
timeout float

Time limit for the axis movement to be done, in seconds. The default is 60 seconds.

60

Raises:

Type Description
TimeoutError

Error when axis does not reach its target position in due time.

Returns:

Type Description
None.
Source code in borealis/controller/controller_base.py
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
def wait_motion_end(self, axis_id: str, target: float, timeout: float = 60):
    """
    Check that the axis has reached its target position.

    Parameters
    ----------
    axis_id : str
        axis ID as registered in the controller object
    target : float
        Position (dial) the axis must reach.
    timeout : float, optional
        Time limit for the axis movement to be done, in seconds.
        The default is 60 seconds.

    Raises
    ------
    TimeoutError
        Error when axis does not reach its target position in due time.

    Returns
    -------
    None.

    """
    sleep_for = 0.1  # sec
    start_time = time.time()
    while True:
        current = self.get_axis_position(axis_id)
        if current == pytest.approx(target, abs=5e-4):
            break

        if self.is_limit_switch_activated(axis_id):
            raise RuntimeError(
                "Limit switch activated, move aborted")

        if (time.time() - start_time) > timeout:
            raise TimeoutError(
                f"Axis never reached target position. Stopped at {current})")

        time.sleep(sleep_for)

DummyCtrl(alias='Dummy Controller')

Bases: Controller

When in need for a controller but no access to a real device.

Source code in borealis/controller/controller_base.py
181
182
183
184
def __init__(self, alias: str = "Dummy Controller") -> None:
    super().__init__(alias=alias)
    self.position = {}
    LOGGER.info('%s initialised.', self)