ephys-link 1.2.8__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ephys_link/__about__.py +1 -1
- ephys_link/common.py +11 -188
- ephys_link/platform_handler.py +112 -128
- ephys_link/platform_manipulator.py +1 -0
- ephys_link/platforms/new_scale_handler.py +43 -37
- ephys_link/platforms/new_scale_manipulator.py +68 -69
- ephys_link/platforms/new_scale_pathfinder_handler.py +33 -32
- ephys_link/platforms/sensapex_handler.py +39 -36
- ephys_link/platforms/sensapex_manipulator.py +57 -62
- ephys_link/platforms/ump3_handler.py +17 -15
- ephys_link/platforms/ump3_manipulator.py +44 -42
- ephys_link/server.py +53 -61
- {ephys_link-1.2.8.dist-info → ephys_link-1.3.0.dist-info}/METADATA +7 -7
- ephys_link-1.3.0.dist-info/RECORD +26 -0
- {ephys_link-1.2.8.dist-info → ephys_link-1.3.0.dist-info}/WHEEL +1 -1
- ephys_link-1.2.8.dist-info/RECORD +0 -26
- {ephys_link-1.2.8.dist-info → ephys_link-1.3.0.dist-info}/entry_points.txt +0 -0
- {ephys_link-1.2.8.dist-info → ephys_link-1.3.0.dist-info}/licenses/LICENSE +0 -0
ephys_link/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.3.0"
|
ephys_link/common.py
CHANGED
|
@@ -6,8 +6,10 @@ callback parameters)
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
import
|
|
10
|
-
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from vbl_aquarium.models.unity import Vector4
|
|
11
13
|
|
|
12
14
|
# Debugging flag
|
|
13
15
|
DEBUG = False
|
|
@@ -36,191 +38,12 @@ def dprint(message: str) -> None:
|
|
|
36
38
|
print(message)
|
|
37
39
|
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"""Data format for positional requests.
|
|
42
|
-
|
|
43
|
-
:param manipulator_id: ID of the manipulator to move.
|
|
44
|
-
:type manipulator_id: str
|
|
45
|
-
:param pos: Position to move to in mm (X, Y, Z, W).
|
|
46
|
-
:type pos: list[float]
|
|
47
|
-
:param speed: Speed to move at in mm/s.
|
|
48
|
-
:type speed: float
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
manipulator_id: str
|
|
52
|
-
pos: list[float]
|
|
53
|
-
speed: float
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class InsideBrainInputDataFormat(TypedDict):
|
|
57
|
-
"""Data format for setting inside brain state.
|
|
58
|
-
|
|
59
|
-
:param manipulator_id: ID of the manipulator to move.
|
|
60
|
-
:type manipulator_id: str
|
|
61
|
-
:param inside: Whether the manipulator is inside the brain.
|
|
62
|
-
:type inside: bool
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
manipulator_id: str
|
|
66
|
-
inside: bool
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class DriveToDepthInputDataFormat(TypedDict):
|
|
70
|
-
"""Data format for depth driving requests.
|
|
71
|
-
|
|
72
|
-
:param manipulator_id: ID of the manipulator to move.
|
|
73
|
-
:type manipulator_id: str
|
|
74
|
-
:param depth: Depth to drive to in mm.
|
|
75
|
-
:type depth: float
|
|
76
|
-
:param speed: Speed to drive at in mm/s.
|
|
77
|
-
:type speed: float
|
|
78
|
-
"""
|
|
79
|
-
|
|
80
|
-
manipulator_id: str
|
|
81
|
-
depth: float
|
|
82
|
-
speed: float
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class CanWriteInputDataFormat(TypedDict):
|
|
86
|
-
"""Data format for setting can write state.
|
|
87
|
-
|
|
88
|
-
:param manipulator_id: ID of the manipulator to move.
|
|
89
|
-
:type manipulator_id: str
|
|
90
|
-
:param can_write: Whether the manipulator can write.
|
|
91
|
-
:type can_write: bool
|
|
92
|
-
:param hours: Number of hours the manipulator can write for.
|
|
93
|
-
:type hours: float
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
manipulator_id: str
|
|
97
|
-
can_write: bool
|
|
98
|
-
hours: float
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# Output data dictionaries
|
|
102
|
-
class GetManipulatorsOutputData(dict):
|
|
103
|
-
"""Output format for get manipulators request.
|
|
104
|
-
|
|
105
|
-
:param manipulators: List of manipulator IDs (as strings).
|
|
106
|
-
:type manipulators: list
|
|
107
|
-
:param num_axes: Number of axes this manipulator has.
|
|
108
|
-
:type num_axes: int
|
|
109
|
-
:param dimensions: Size of the movement space in mm (first 3 axes).
|
|
110
|
-
:type dimensions: list
|
|
111
|
-
:param error: Error message.
|
|
112
|
-
:type error: str
|
|
113
|
-
|
|
114
|
-
:example: Example generated dictionary
|
|
115
|
-
:code:`{"manipulators": ["1", "2"], "num_axes": 4, "dimensions": [20, 20, 20], "error": ""}`
|
|
116
|
-
"""
|
|
117
|
-
|
|
118
|
-
def __init__(self, manipulators: list, num_axes: int, dimensions: list, error: str) -> None:
|
|
119
|
-
"""Constructor"""
|
|
120
|
-
super().__init__(
|
|
121
|
-
manipulators=manipulators,
|
|
122
|
-
num_axes=num_axes,
|
|
123
|
-
dimensions=dimensions,
|
|
124
|
-
error=error,
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
def json(self) -> str:
|
|
128
|
-
"""Return JSON string"""
|
|
129
|
-
return json.dumps(self)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class PositionalOutputData(dict):
|
|
133
|
-
"""Output format for positional requests.
|
|
134
|
-
|
|
135
|
-
:param position: Position in mm (as a list, empty on error) in X, Y, Z, W order.
|
|
136
|
-
:type position: list
|
|
137
|
-
:param error: Error message.
|
|
138
|
-
:type error: str
|
|
41
|
+
def vector4_to_array(vector4: Vector4) -> list[float]:
|
|
42
|
+
"""Convert a Vector4 to a list of floats.
|
|
139
43
|
|
|
140
|
-
:
|
|
141
|
-
|
|
44
|
+
:param vector4: Vector4 to convert.
|
|
45
|
+
:type vector4: :class:`vbl_aquarium.models.unity.Vector4`
|
|
46
|
+
:return: List of floats.
|
|
47
|
+
:rtype: list[float]
|
|
142
48
|
"""
|
|
143
|
-
|
|
144
|
-
def __init__(self, position: list, error: str) -> None:
|
|
145
|
-
"""Constructor"""
|
|
146
|
-
super().__init__(position=position, error=error)
|
|
147
|
-
|
|
148
|
-
def json(self) -> str:
|
|
149
|
-
"""Return JSON string"""
|
|
150
|
-
return json.dumps(self)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
class AngularOutputData(dict):
|
|
154
|
-
"""Output format for manipulator angle requests.
|
|
155
|
-
|
|
156
|
-
:param angles: Angles in degrees (as a list, can be empty) in yaw, pitch, roll order.
|
|
157
|
-
:type angles: list
|
|
158
|
-
:param error: Error message.
|
|
159
|
-
:type error: str
|
|
160
|
-
"""
|
|
161
|
-
|
|
162
|
-
def __init__(self, angles: list, error: str) -> None:
|
|
163
|
-
"""Constructor"""
|
|
164
|
-
super().__init__(angles=angles, error=error)
|
|
165
|
-
|
|
166
|
-
def json(self) -> str:
|
|
167
|
-
"""Return JSON string"""
|
|
168
|
-
return json.dumps(self)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
class ShankCountOutputData(dict):
|
|
172
|
-
"""Output format for number of shanks.
|
|
173
|
-
|
|
174
|
-
:param shank_count: Number of shanks on the probe (-1 if error).
|
|
175
|
-
:type shank_count: int
|
|
176
|
-
:param error: Error message.
|
|
177
|
-
:type error: str
|
|
178
|
-
"""
|
|
179
|
-
|
|
180
|
-
def __init__(self, shank_count: int, error: str) -> None:
|
|
181
|
-
"""Constructor"""
|
|
182
|
-
super().__init__(shank_count=shank_count, error=error)
|
|
183
|
-
|
|
184
|
-
def json(self) -> str:
|
|
185
|
-
"""Return JSON string"""
|
|
186
|
-
return json.dumps(self)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
class DriveToDepthOutputData(dict):
|
|
190
|
-
"""Output format for depth driving.
|
|
191
|
-
|
|
192
|
-
:param depth: Depth in mm (0 on error).
|
|
193
|
-
:type depth: float
|
|
194
|
-
:param error: Error message.
|
|
195
|
-
:type error: str
|
|
196
|
-
|
|
197
|
-
:example: Example generated dictionary :code:`{"depth": 1.23, "error": ""}`
|
|
198
|
-
"""
|
|
199
|
-
|
|
200
|
-
def __init__(self, depth: float, error: str) -> None:
|
|
201
|
-
"""Create drive to depth output data dictionary"""
|
|
202
|
-
super().__init__(depth=depth, error=error)
|
|
203
|
-
|
|
204
|
-
def json(self) -> str:
|
|
205
|
-
"""Return JSON string"""
|
|
206
|
-
return json.dumps(self)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
class StateOutputData(dict):
|
|
210
|
-
"""Output format for boolean state requests.
|
|
211
|
-
|
|
212
|
-
:param state: State of the event.
|
|
213
|
-
:type state: bool
|
|
214
|
-
:param error: Error message.
|
|
215
|
-
:type error: str
|
|
216
|
-
|
|
217
|
-
:example: Example generated dictionary :code:`{"state": True, "error": ""}`
|
|
218
|
-
"""
|
|
219
|
-
|
|
220
|
-
def __init__(self, state: bool, error: str) -> None:
|
|
221
|
-
"""Create state output data dictionary"""
|
|
222
|
-
super().__init__(state=state, error=error)
|
|
223
|
-
|
|
224
|
-
def json(self) -> str:
|
|
225
|
-
"""Return JSON string"""
|
|
226
|
-
return json.dumps(self)
|
|
49
|
+
return [vector4.x, vector4.y, vector4.z, vector4.w]
|
ephys_link/platform_handler.py
CHANGED
|
@@ -17,6 +17,20 @@ from __future__ import annotations
|
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
18
|
from typing import TYPE_CHECKING
|
|
19
19
|
|
|
20
|
+
from vbl_aquarium.models.ephys_link import (
|
|
21
|
+
AngularResponse,
|
|
22
|
+
BooleanStateResponse,
|
|
23
|
+
CanWriteRequest,
|
|
24
|
+
DriveToDepthRequest,
|
|
25
|
+
DriveToDepthResponse,
|
|
26
|
+
GetManipulatorsResponse,
|
|
27
|
+
GotoPositionRequest,
|
|
28
|
+
InsideBrainRequest,
|
|
29
|
+
PositionalResponse,
|
|
30
|
+
ShankCountResponse,
|
|
31
|
+
)
|
|
32
|
+
from vbl_aquarium.models.unity import Vector4
|
|
33
|
+
|
|
20
34
|
from ephys_link import common as com
|
|
21
35
|
|
|
22
36
|
if TYPE_CHECKING:
|
|
@@ -30,14 +44,14 @@ class PlatformHandler(ABC):
|
|
|
30
44
|
"""Initialize the manipulator handler with a dictionary of manipulators."""
|
|
31
45
|
|
|
32
46
|
# Registered manipulators are stored as a dictionary of IDs (string) to
|
|
33
|
-
# manipulator objects
|
|
47
|
+
# manipulator objects.
|
|
34
48
|
self.manipulators = {}
|
|
35
49
|
self.num_axes = 4
|
|
36
50
|
|
|
37
51
|
# Platform axes dimensions in mm
|
|
38
|
-
self.dimensions =
|
|
52
|
+
self.dimensions = Vector4(x=20, y=20, z=20, w=20)
|
|
39
53
|
|
|
40
|
-
# Platform Handler Methods
|
|
54
|
+
# Platform Handler Methods.
|
|
41
55
|
|
|
42
56
|
def reset(self) -> bool:
|
|
43
57
|
"""Reset handler.
|
|
@@ -65,21 +79,21 @@ class PlatformHandler(ABC):
|
|
|
65
79
|
else:
|
|
66
80
|
return True
|
|
67
81
|
|
|
68
|
-
def get_manipulators(self) ->
|
|
82
|
+
def get_manipulators(self) -> GetManipulatorsResponse:
|
|
69
83
|
"""Get all registered manipulators.
|
|
70
84
|
|
|
71
85
|
:return: Result of connected manipulators, platform information, and error message (if any).
|
|
72
|
-
:rtype: :class:`ephys_link.
|
|
86
|
+
:rtype: :class:`vbl_aquarium.models.ephys_link.GetManipulatorsResponse`
|
|
73
87
|
"""
|
|
74
|
-
error = "Error getting manipulators"
|
|
75
88
|
try:
|
|
76
|
-
|
|
77
|
-
error = ""
|
|
89
|
+
manipulators = self._get_manipulators()
|
|
78
90
|
except Exception as e:
|
|
79
91
|
print(f"[ERROR]\t\t Getting manipulators: {type(e)}: {e}\n")
|
|
80
|
-
return
|
|
92
|
+
return GetManipulatorsResponse(error="Error getting manipulators")
|
|
81
93
|
else:
|
|
82
|
-
return
|
|
94
|
+
return GetManipulatorsResponse(
|
|
95
|
+
manipulators=manipulators, num_axes=self.num_axes, dimensions=self.dimensions
|
|
96
|
+
)
|
|
83
97
|
|
|
84
98
|
def register_manipulator(self, manipulator_id: str) -> str:
|
|
85
99
|
"""Register a manipulator.
|
|
@@ -135,46 +149,46 @@ class PlatformHandler(ABC):
|
|
|
135
149
|
com.dprint(f"[SUCCESS]\t Unregistered manipulator: {manipulator_id}\n")
|
|
136
150
|
return ""
|
|
137
151
|
|
|
138
|
-
def get_pos(self, manipulator_id: str) ->
|
|
152
|
+
def get_pos(self, manipulator_id: str) -> PositionalResponse:
|
|
139
153
|
"""Get the current position of a manipulator.
|
|
140
154
|
|
|
141
155
|
:param manipulator_id: The ID of the manipulator to get the position of.
|
|
142
156
|
:type manipulator_id: str
|
|
143
157
|
:return: Positional information for the manipulator and error message (if any).
|
|
144
|
-
:rtype: :class:`ephys_link.
|
|
158
|
+
:rtype: :class:`vbl_aquarium.models.ephys_link.PositionalResponse`
|
|
145
159
|
"""
|
|
146
160
|
try:
|
|
147
|
-
# Check calibration status
|
|
161
|
+
# Check calibration status.
|
|
148
162
|
if (
|
|
149
163
|
hasattr(self.manipulators[manipulator_id], "get_calibrated")
|
|
150
164
|
and not self.manipulators[manipulator_id].get_calibrated()
|
|
151
165
|
):
|
|
152
166
|
print(f"[ERROR]\t\t Calibration not complete: {manipulator_id}\n")
|
|
153
|
-
return
|
|
167
|
+
return PositionalResponse(error="Manipulator not calibrated")
|
|
154
168
|
|
|
155
|
-
# Get position and convert to unified space
|
|
169
|
+
# Get position and convert to unified space.
|
|
156
170
|
manipulator_pos = self._get_pos(manipulator_id)
|
|
157
171
|
|
|
158
|
-
# Shortcut return for Pathfinder
|
|
172
|
+
# Shortcut return for Pathfinder.
|
|
159
173
|
if self.num_axes == -1:
|
|
160
174
|
return manipulator_pos
|
|
161
175
|
|
|
162
|
-
#
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
176
|
+
# Convert position to unified space.
|
|
177
|
+
return manipulator_pos.model_copy(
|
|
178
|
+
update={"position": self._platform_space_to_unified_space(manipulator_pos.position)}
|
|
179
|
+
)
|
|
166
180
|
except KeyError:
|
|
167
|
-
# Manipulator not found in registered manipulators
|
|
181
|
+
# Manipulator not found in registered manipulators.
|
|
168
182
|
print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}")
|
|
169
|
-
return
|
|
183
|
+
return PositionalResponse(error="Manipulator not registered")
|
|
170
184
|
|
|
171
|
-
def get_angles(self, manipulator_id: str) ->
|
|
185
|
+
def get_angles(self, manipulator_id: str) -> AngularResponse:
|
|
172
186
|
"""Get the current position of a manipulator.
|
|
173
187
|
|
|
174
188
|
:param manipulator_id: The ID of the manipulator to get the angles of.
|
|
175
189
|
:type manipulator_id: str
|
|
176
190
|
:return: Angular information for the manipulator and error message (if any).
|
|
177
|
-
:rtype: :class:`ephys_link.
|
|
191
|
+
:rtype: :class:`vbl_aquarium.models.ephys_link.AngularResponse`
|
|
178
192
|
"""
|
|
179
193
|
try:
|
|
180
194
|
# Check calibration status
|
|
@@ -183,7 +197,7 @@ class PlatformHandler(ABC):
|
|
|
183
197
|
and not self.manipulators[manipulator_id].get_calibrated()
|
|
184
198
|
):
|
|
185
199
|
print(f"[ERROR]\t\t Calibration not complete: {manipulator_id}\n")
|
|
186
|
-
return
|
|
200
|
+
return AngularResponse(error="Manipulator not calibrated")
|
|
187
201
|
|
|
188
202
|
# Get position
|
|
189
203
|
return self._get_angles(manipulator_id)
|
|
@@ -191,124 +205,109 @@ class PlatformHandler(ABC):
|
|
|
191
205
|
except KeyError:
|
|
192
206
|
# Manipulator not found in registered manipulators
|
|
193
207
|
print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}")
|
|
194
|
-
return
|
|
208
|
+
return AngularResponse(error="Manipulator not registered")
|
|
195
209
|
|
|
196
|
-
def get_shank_count(self, manipulator_id: str) ->
|
|
210
|
+
def get_shank_count(self, manipulator_id: str) -> ShankCountResponse:
|
|
197
211
|
"""Get the number of shanks on the probe
|
|
198
212
|
|
|
199
213
|
:param manipulator_id: The ID of the manipulator to get the number of shanks of.
|
|
200
214
|
:type manipulator_id: str
|
|
201
215
|
:return: Number of shanks on the probe.
|
|
202
|
-
:rtype: :class:`ephys_link.
|
|
216
|
+
:rtype: :class:`vbl_aquarium.models.ephys_link.ShankCountResponse`
|
|
203
217
|
"""
|
|
204
218
|
return self._get_shank_count(manipulator_id)
|
|
205
219
|
|
|
206
|
-
async def goto_pos(self,
|
|
220
|
+
async def goto_pos(self, request: GotoPositionRequest) -> PositionalResponse:
|
|
207
221
|
"""Move manipulator to position
|
|
208
222
|
|
|
209
|
-
:param
|
|
210
|
-
:type
|
|
211
|
-
:param position: The position to move to in (x, y, z, w) in mm
|
|
212
|
-
:type position: list[float]
|
|
213
|
-
:param speed: The speed to move at (in mm/s)
|
|
214
|
-
:type speed: float
|
|
223
|
+
:param request: The goto request parsed from the server.
|
|
224
|
+
:type request: :class:`vbl_aquarium.models.ephys_link.GotoPositionRequest`
|
|
215
225
|
:return: Resulting position of the manipulator and error message (if any).
|
|
216
|
-
:rtype: :class:`ephys_link.
|
|
226
|
+
:rtype: :class:`vbl_aquarium.models.ephys_link.PositionalResponse`
|
|
217
227
|
"""
|
|
218
228
|
try:
|
|
219
|
-
# Check calibration status
|
|
220
|
-
if not self.manipulators[manipulator_id].get_calibrated():
|
|
221
|
-
print(f"[ERROR]\t\t Calibration not complete: {manipulator_id}\n")
|
|
222
|
-
return
|
|
229
|
+
# Check calibration status.
|
|
230
|
+
if not self.manipulators[request.manipulator_id].get_calibrated():
|
|
231
|
+
print(f"[ERROR]\t\t Calibration not complete: {request.manipulator_id}\n")
|
|
232
|
+
return PositionalResponse(error="Manipulator not calibrated")
|
|
223
233
|
|
|
224
|
-
# Check write state
|
|
225
|
-
if not self.manipulators[manipulator_id].get_can_write():
|
|
226
|
-
print(f"[ERROR]\t\t Cannot write to manipulator: {manipulator_id}")
|
|
227
|
-
return
|
|
234
|
+
# Check write state.
|
|
235
|
+
if not self.manipulators[request.manipulator_id].get_can_write():
|
|
236
|
+
print(f"[ERROR]\t\t Cannot write to manipulator: {request.manipulator_id}")
|
|
237
|
+
return PositionalResponse(error="Cannot write to manipulator")
|
|
228
238
|
|
|
229
239
|
# Convert position to platform space, move, and convert final position back to
|
|
230
|
-
# unified space
|
|
231
|
-
end_position = await self._goto_pos(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return
|
|
235
|
-
|
|
240
|
+
# unified space.
|
|
241
|
+
end_position = await self._goto_pos(
|
|
242
|
+
request.model_copy(update={"position": self._unified_space_to_platform_space(request.position)})
|
|
243
|
+
)
|
|
244
|
+
return end_position.model_copy(
|
|
245
|
+
update={"position": self._platform_space_to_unified_space(end_position.position)}
|
|
246
|
+
)
|
|
236
247
|
except KeyError:
|
|
237
|
-
# Manipulator not found in registered manipulators
|
|
238
|
-
print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}\n")
|
|
239
|
-
return
|
|
248
|
+
# Manipulator not found in registered manipulators.
|
|
249
|
+
print(f"[ERROR]\t\t Manipulator not registered: {request.manipulator_id}\n")
|
|
250
|
+
return PositionalResponse(error="Manipulator not registered")
|
|
240
251
|
|
|
241
|
-
async def drive_to_depth(self,
|
|
252
|
+
async def drive_to_depth(self, request: DriveToDepthRequest) -> DriveToDepthResponse:
|
|
242
253
|
"""Drive manipulator to depth
|
|
243
254
|
|
|
244
|
-
:param
|
|
245
|
-
:type
|
|
246
|
-
:param depth: The depth to drive to in mm
|
|
247
|
-
:type depth: float
|
|
248
|
-
:param speed: The speed to drive at (in mm/s)
|
|
249
|
-
:type speed: float
|
|
255
|
+
:param request: The drive to depth request parsed from the server.
|
|
256
|
+
:type request: :class:`vbl_aquarium.models.ephys_link.DriveToDepthRequest`
|
|
250
257
|
:return: Resulting depth of the manipulator and error message (if any).
|
|
251
258
|
:rtype: :class:`ephys_link.common.DriveToDepthOutputData`
|
|
252
259
|
"""
|
|
253
260
|
try:
|
|
254
261
|
# Check calibration status
|
|
255
|
-
if not self.manipulators[manipulator_id].get_calibrated():
|
|
256
|
-
print(f"[ERROR]\t\t Calibration not complete: {manipulator_id}\n")
|
|
257
|
-
return
|
|
262
|
+
if not self.manipulators[request.manipulator_id].get_calibrated():
|
|
263
|
+
print(f"[ERROR]\t\t Calibration not complete: {request.manipulator_id}\n")
|
|
264
|
+
return DriveToDepthResponse(error="Manipulator not calibrated")
|
|
258
265
|
|
|
259
266
|
# Check write state
|
|
260
|
-
if not self.manipulators[manipulator_id].get_can_write():
|
|
261
|
-
print(f"[ERROR]\t\t Cannot write to manipulator: {manipulator_id}")
|
|
262
|
-
return
|
|
267
|
+
if not self.manipulators[request.manipulator_id].get_can_write():
|
|
268
|
+
print(f"[ERROR]\t\t Cannot write to manipulator: {request.manipulator_id}")
|
|
269
|
+
return DriveToDepthResponse(error="Cannot write to manipulator")
|
|
263
270
|
|
|
264
271
|
end_depth = await self._drive_to_depth(
|
|
265
|
-
|
|
266
|
-
self._unified_space_to_platform_space([0, 0, 0, depth])[3],
|
|
267
|
-
speed,
|
|
272
|
+
request.model_copy(update={"depth": self._unified_space_to_platform_space(Vector4(w=request.depth)).w})
|
|
268
273
|
)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return com.DriveToDepthOutputData(
|
|
272
|
-
self._platform_space_to_unified_space([0, 0, 0, end_depth["depth"]])[3],
|
|
273
|
-
"",
|
|
274
|
+
return end_depth.model_copy(
|
|
275
|
+
update={"depth": self._platform_space_to_unified_space(Vector4(w=end_depth.depth)).w}
|
|
274
276
|
)
|
|
275
|
-
|
|
276
277
|
except KeyError:
|
|
277
278
|
# Manipulator not found in registered manipulators
|
|
278
|
-
print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}\n")
|
|
279
|
-
return
|
|
279
|
+
print(f"[ERROR]\t\t Manipulator not registered: {request.manipulator_id}\n")
|
|
280
|
+
return DriveToDepthResponse(error="Manipulator not registered")
|
|
280
281
|
|
|
281
|
-
def set_inside_brain(self,
|
|
282
|
+
def set_inside_brain(self, request: InsideBrainRequest) -> BooleanStateResponse:
|
|
282
283
|
"""Set manipulator inside brain state (restricts motion)
|
|
283
284
|
|
|
284
|
-
:param
|
|
285
|
-
:type
|
|
286
|
-
:param inside: True if inside brain, False if outside
|
|
287
|
-
:type inside: bool
|
|
285
|
+
:param request: The inside brain request parsed from the server.
|
|
286
|
+
:type request: :class:`vbl_aquarium.models.ephys_link.InsideBrainRequest`
|
|
288
287
|
:return: New inside brain state of the manipulator and error message (if any).
|
|
289
|
-
:rtype: :class:`ephys_link.
|
|
288
|
+
:rtype: :class:`vbl_aquarium.models.ephys_link.BooleanStateResponse`
|
|
290
289
|
"""
|
|
291
290
|
try:
|
|
292
291
|
# Check calibration status
|
|
293
292
|
if (
|
|
294
|
-
hasattr(self.manipulators[manipulator_id], "get_calibrated")
|
|
295
|
-
and not self.manipulators[manipulator_id].get_calibrated()
|
|
293
|
+
hasattr(self.manipulators[request.manipulator_id], "get_calibrated")
|
|
294
|
+
and not self.manipulators[request.manipulator_id].get_calibrated()
|
|
296
295
|
):
|
|
297
296
|
print("[ERROR]\t\t Calibration not complete\n")
|
|
298
|
-
return
|
|
297
|
+
return BooleanStateResponse(error="Manipulator not calibrated")
|
|
299
298
|
|
|
300
|
-
return self._set_inside_brain(
|
|
299
|
+
return self._set_inside_brain(request)
|
|
301
300
|
|
|
302
301
|
except KeyError:
|
|
303
302
|
# Manipulator not found in registered manipulators
|
|
304
|
-
print(f"[ERROR]\t\t Manipulator {manipulator_id} not registered\n")
|
|
305
|
-
return
|
|
303
|
+
print(f"[ERROR]\t\t Manipulator {request.manipulator_id} not registered\n")
|
|
304
|
+
return BooleanStateResponse(error="Manipulator not " "registered")
|
|
306
305
|
|
|
307
306
|
except Exception as e:
|
|
308
307
|
# Other error
|
|
309
|
-
print(f"[ERROR]\t\t Set manipulator {manipulator_id} inside brain " f"state")
|
|
308
|
+
print(f"[ERROR]\t\t Set manipulator {request.manipulator_id} inside brain " f"state")
|
|
310
309
|
print(f"{e}\n")
|
|
311
|
-
return
|
|
310
|
+
return BooleanStateResponse(error="Error setting inside brain")
|
|
312
311
|
|
|
313
312
|
async def calibrate(self, manipulator_id: str, sio: socketio.AsyncServer) -> str:
|
|
314
313
|
"""Calibrate manipulator
|
|
@@ -364,35 +363,26 @@ class PlatformHandler(ABC):
|
|
|
364
363
|
|
|
365
364
|
def set_can_write(
|
|
366
365
|
self,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
hours: float,
|
|
370
|
-
sio: socketio.AsyncServer,
|
|
371
|
-
) -> com.StateOutputData:
|
|
366
|
+
request: CanWriteRequest,
|
|
367
|
+
) -> BooleanStateResponse:
|
|
372
368
|
"""Set manipulator can_write state (enables/disabled moving manipulator)
|
|
373
369
|
|
|
374
|
-
:param
|
|
375
|
-
:type
|
|
376
|
-
:param can_write: True if allowed to move, False if outside
|
|
377
|
-
:type can_write: bool
|
|
378
|
-
:param hours: The number of hours to allow writing (0 = forever)
|
|
379
|
-
:type hours: float
|
|
380
|
-
:param sio: SocketIO object from server to emit reset event
|
|
381
|
-
:type sio: :class:`socketio.AsyncServer`
|
|
370
|
+
:param request: The can write request parsed from the server.
|
|
371
|
+
:type request: :class:`vbl_aquarium.models.ephys_link.CanWriteRequest`
|
|
382
372
|
:return: New can_write state of the manipulator and error message (if any).
|
|
383
373
|
:rtype: :class:`ephys_link.common.StateOutputData`
|
|
384
374
|
"""
|
|
385
375
|
try:
|
|
386
|
-
return self._set_can_write(
|
|
376
|
+
return self._set_can_write(request)
|
|
387
377
|
except KeyError:
|
|
388
378
|
# Manipulator not found in registered manipulators
|
|
389
|
-
print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}\n")
|
|
390
|
-
return
|
|
379
|
+
print(f"[ERROR]\t\t Manipulator not registered: {request.manipulator_id}\n")
|
|
380
|
+
return BooleanStateResponse(error="Manipulator not registered")
|
|
391
381
|
except Exception as e:
|
|
392
382
|
# Other error
|
|
393
|
-
print(f"[ERROR]\t\t Set manipulator {manipulator_id} can_write state")
|
|
383
|
+
print(f"[ERROR]\t\t Set manipulator {request.manipulator_id} can_write state")
|
|
394
384
|
print(f"{e}\n")
|
|
395
|
-
return
|
|
385
|
+
return BooleanStateResponse(error="Error setting can_write")
|
|
396
386
|
|
|
397
387
|
# Platform specific methods to override
|
|
398
388
|
|
|
@@ -409,27 +399,27 @@ class PlatformHandler(ABC):
|
|
|
409
399
|
raise NotImplementedError
|
|
410
400
|
|
|
411
401
|
@abstractmethod
|
|
412
|
-
def _get_pos(self, manipulator_id: str) ->
|
|
402
|
+
def _get_pos(self, manipulator_id: str) -> PositionalResponse:
|
|
413
403
|
raise NotImplementedError
|
|
414
404
|
|
|
415
405
|
@abstractmethod
|
|
416
|
-
def _get_angles(self, manipulator_id: str) ->
|
|
406
|
+
def _get_angles(self, manipulator_id: str) -> AngularResponse:
|
|
417
407
|
raise NotImplementedError
|
|
418
408
|
|
|
419
409
|
@abstractmethod
|
|
420
|
-
def _get_shank_count(self, manipulator_id: str) ->
|
|
410
|
+
def _get_shank_count(self, manipulator_id: str) -> ShankCountResponse:
|
|
421
411
|
raise NotImplementedError
|
|
422
412
|
|
|
423
413
|
@abstractmethod
|
|
424
|
-
async def _goto_pos(self,
|
|
414
|
+
async def _goto_pos(self, request: GotoPositionRequest) -> PositionalResponse:
|
|
425
415
|
raise NotImplementedError
|
|
426
416
|
|
|
427
417
|
@abstractmethod
|
|
428
|
-
async def _drive_to_depth(self,
|
|
418
|
+
async def _drive_to_depth(self, request: DriveToDepthRequest) -> DriveToDepthResponse:
|
|
429
419
|
raise NotImplementedError
|
|
430
420
|
|
|
431
421
|
@abstractmethod
|
|
432
|
-
def _set_inside_brain(self,
|
|
422
|
+
def _set_inside_brain(self, request: InsideBrainRequest) -> BooleanStateResponse:
|
|
433
423
|
raise NotImplementedError
|
|
434
424
|
|
|
435
425
|
@abstractmethod
|
|
@@ -449,33 +439,27 @@ class PlatformHandler(ABC):
|
|
|
449
439
|
raise NotImplementedError
|
|
450
440
|
|
|
451
441
|
@abstractmethod
|
|
452
|
-
def _set_can_write(
|
|
453
|
-
self,
|
|
454
|
-
manipulator_id: str,
|
|
455
|
-
can_write: bool,
|
|
456
|
-
hours: float,
|
|
457
|
-
sio: socketio.AsyncServer,
|
|
458
|
-
) -> com.StateOutputData:
|
|
442
|
+
def _set_can_write(self, request: CanWriteRequest) -> BooleanStateResponse:
|
|
459
443
|
raise NotImplementedError
|
|
460
444
|
|
|
461
445
|
@abstractmethod
|
|
462
|
-
def _platform_space_to_unified_space(self, platform_position:
|
|
446
|
+
def _platform_space_to_unified_space(self, platform_position: Vector4) -> Vector4:
|
|
463
447
|
"""Convert position in platform space to position in unified manipulator space
|
|
464
448
|
|
|
465
449
|
:param platform_position: Position in platform space (x, y, z, w) in mm
|
|
466
|
-
:type platform_position:
|
|
450
|
+
:type platform_position: Vector4
|
|
467
451
|
:return: Position in unified manipulator space (x, y, z, w) in mm
|
|
468
|
-
:rtype:
|
|
452
|
+
:rtype: Vector4
|
|
469
453
|
"""
|
|
470
454
|
raise NotImplementedError
|
|
471
455
|
|
|
472
456
|
@abstractmethod
|
|
473
|
-
def _unified_space_to_platform_space(self, unified_position:
|
|
457
|
+
def _unified_space_to_platform_space(self, unified_position: Vector4) -> Vector4:
|
|
474
458
|
"""Convert position in unified manipulator space to position in platform space
|
|
475
459
|
|
|
476
460
|
:param unified_position: Position in unified manipulator space (x, y, z, w) in mm
|
|
477
|
-
:type unified_position:
|
|
461
|
+
:type unified_position: Vector4
|
|
478
462
|
:return: Position in platform space (x, y, z, w) in mm
|
|
479
|
-
:rtype:
|
|
463
|
+
:rtype: Vector4
|
|
480
464
|
"""
|
|
481
465
|
raise NotImplementedError
|