ephys-link 1.3.3__py3-none-any.whl → 2.0.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.
Files changed (42) hide show
  1. ephys_link/__about__.py +1 -1
  2. ephys_link/__main__.py +51 -105
  3. ephys_link/back_end/__init__.py +0 -0
  4. ephys_link/back_end/platform_handler.py +315 -0
  5. ephys_link/back_end/server.py +274 -0
  6. ephys_link/bindings/__init__.py +0 -0
  7. ephys_link/bindings/fake_binding.py +84 -0
  8. ephys_link/bindings/mpm_binding.py +315 -0
  9. ephys_link/bindings/ump_4_binding.py +157 -0
  10. ephys_link/front_end/__init__.py +0 -0
  11. ephys_link/front_end/cli.py +104 -0
  12. ephys_link/front_end/gui.py +204 -0
  13. ephys_link/utils/__init__.py +0 -0
  14. ephys_link/utils/base_binding.py +176 -0
  15. ephys_link/utils/console.py +127 -0
  16. ephys_link/utils/constants.py +23 -0
  17. ephys_link/utils/converters.py +86 -0
  18. ephys_link/utils/startup.py +65 -0
  19. ephys_link-2.0.0.dist-info/METADATA +91 -0
  20. ephys_link-2.0.0.dist-info/RECORD +25 -0
  21. {ephys_link-1.3.3.dist-info → ephys_link-2.0.0.dist-info}/WHEEL +1 -1
  22. {ephys_link-1.3.3.dist-info → ephys_link-2.0.0.dist-info}/licenses/LICENSE +674 -674
  23. ephys_link/common.py +0 -49
  24. ephys_link/emergency_stop.py +0 -67
  25. ephys_link/gui.py +0 -217
  26. ephys_link/platform_handler.py +0 -465
  27. ephys_link/platform_manipulator.py +0 -35
  28. ephys_link/platforms/__init__.py +0 -5
  29. ephys_link/platforms/new_scale_handler.py +0 -141
  30. ephys_link/platforms/new_scale_manipulator.py +0 -312
  31. ephys_link/platforms/new_scale_pathfinder_handler.py +0 -235
  32. ephys_link/platforms/sensapex_handler.py +0 -151
  33. ephys_link/platforms/sensapex_manipulator.py +0 -227
  34. ephys_link/platforms/ump3_handler.py +0 -57
  35. ephys_link/platforms/ump3_manipulator.py +0 -147
  36. ephys_link/resources/CP210xManufacturing.dll +0 -0
  37. ephys_link/resources/NstMotorCtrl.dll +0 -0
  38. ephys_link/resources/SiUSBXp.dll +0 -0
  39. ephys_link/server.py +0 -508
  40. ephys_link-1.3.3.dist-info/METADATA +0 -164
  41. ephys_link-1.3.3.dist-info/RECORD +0 -26
  42. {ephys_link-1.3.3.dist-info → ephys_link-2.0.0.dist-info}/entry_points.txt +0 -0
@@ -1,465 +0,0 @@
1
- """Handle communications with platform specific API
2
-
3
- Handles relaying WebSocket messages to the appropriate platform API functions and
4
- conducting error checks on the input and output values
5
-
6
- Function names here are the same as the WebSocket events. They are called when the
7
- server receives an event from a client. In general, each function does the following:
8
-
9
- 1. Receive extracted arguments from :mod:`ephys_link.server`
10
- 2. Call and check the appropriate platform API function (overloaded by each platform)
11
- 3. Log/handle successes and failures
12
- 4. Return the callback parameters to :mod:`ephys_link.server`
13
- """
14
-
15
- from __future__ import annotations
16
-
17
- from abc import ABC, abstractmethod
18
- from typing import TYPE_CHECKING
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
-
34
- from ephys_link import common as com
35
-
36
- if TYPE_CHECKING:
37
- from socketio import AsyncClient, AsyncServer
38
-
39
-
40
- class PlatformHandler(ABC):
41
- """An abstract class that defines the interface for a manipulator handler."""
42
-
43
- def __init__(self):
44
- """Initialize the manipulator handler with a dictionary of manipulators."""
45
-
46
- # Registered manipulators are stored as a dictionary of IDs (string) to
47
- # manipulator objects.
48
- self.manipulators = {}
49
- self.num_axes = 4
50
-
51
- # Platform axes dimensions in mm
52
- self.dimensions = Vector4(x=20, y=20, z=20, w=20)
53
-
54
- # Platform Handler Methods.
55
-
56
- def reset(self) -> bool:
57
- """Reset handler.
58
-
59
- :return: True if successful, False otherwise.
60
- :rtype: bool
61
- """
62
- stop_result = self.stop()
63
- self.manipulators.clear()
64
- return stop_result
65
-
66
- def stop(self) -> bool:
67
- """Stop handler.
68
-
69
- :return: True if successful, False otherwise.
70
- :rtype: bool
71
- """
72
- try:
73
- for manipulator in self.manipulators.values():
74
- if hasattr(manipulator, "stop"):
75
- manipulator.stop()
76
- except Exception as e:
77
- print(f"[ERROR]\t\t Could not stop manipulators: {e}\n")
78
- return False
79
- else:
80
- return True
81
-
82
- def get_manipulators(self) -> GetManipulatorsResponse:
83
- """Get all registered manipulators.
84
-
85
- :return: Result of connected manipulators, platform information, and error message (if any).
86
- :rtype: :class:`vbl_aquarium.models.ephys_link.GetManipulatorsResponse`
87
- """
88
- try:
89
- manipulators = self._get_manipulators()
90
- except Exception as e:
91
- print(f"[ERROR]\t\t Getting manipulators: {type(e)}: {e}\n")
92
- return GetManipulatorsResponse(error="Error getting manipulators")
93
- else:
94
- return GetManipulatorsResponse(
95
- manipulators=manipulators, num_axes=self.num_axes, dimensions=self.dimensions
96
- )
97
-
98
- def register_manipulator(self, manipulator_id: str) -> str:
99
- """Register a manipulator.
100
-
101
- :param manipulator_id: The ID of the manipulator to register.
102
- :type manipulator_id: str
103
- :return: Error message on error, empty string otherwise.
104
- :rtype: str
105
- """
106
- # Check if manipulator is already registered
107
- if manipulator_id in self.manipulators:
108
- print(f"[ERROR]\t\t Manipulator already registered:" f" {manipulator_id}\n")
109
- return "Manipulator already registered"
110
-
111
- try:
112
- # Register manipulator
113
- self._register_manipulator(manipulator_id)
114
- except ValueError as ve:
115
- # Manipulator not found in UMP
116
- print(f"[ERROR]\t\t Manipulator not found: {manipulator_id}: {ve}\n")
117
- return "Manipulator not found"
118
- except Exception as e:
119
- # Other error
120
- print(f"[ERROR]\t\t Registering manipulator: {manipulator_id}")
121
- print(f"{type(e)}: {e}\n")
122
- return "Error registering manipulator"
123
- else:
124
- com.dprint(f"[SUCCESS]\t Registered manipulator: {manipulator_id}\n")
125
- return ""
126
-
127
- def unregister_manipulator(self, manipulator_id: str) -> str:
128
- """Unregister a manipulator.
129
-
130
- :param manipulator_id: The ID of the manipulator to unregister.
131
- :type manipulator_id: str
132
- :return: Error message on error, empty string otherwise.
133
- :rtype: str
134
- """
135
- # Check if manipulator is not registered
136
- if manipulator_id not in self.manipulators:
137
- print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}\n")
138
- return "Manipulator not registered"
139
-
140
- try:
141
- # Unregister manipulator
142
- self._unregister_manipulator(manipulator_id)
143
- except Exception as e:
144
- # Other error
145
- print(f"[ERROR]\t\t Unregistering manipulator: {manipulator_id}")
146
- print(f"{e}\n")
147
- return "Error unregistering manipulator"
148
- else:
149
- com.dprint(f"[SUCCESS]\t Unregistered manipulator: {manipulator_id}\n")
150
- return ""
151
-
152
- def get_pos(self, manipulator_id: str) -> PositionalResponse:
153
- """Get the current position of a manipulator.
154
-
155
- :param manipulator_id: The ID of the manipulator to get the position of.
156
- :type manipulator_id: str
157
- :return: Positional information for the manipulator and error message (if any).
158
- :rtype: :class:`vbl_aquarium.models.ephys_link.PositionalResponse`
159
- """
160
- try:
161
- # Check calibration status.
162
- if (
163
- hasattr(self.manipulators[manipulator_id], "get_calibrated")
164
- and not self.manipulators[manipulator_id].get_calibrated()
165
- ):
166
- print(f"[ERROR]\t\t Calibration not complete: {manipulator_id}\n")
167
- return PositionalResponse(error="Manipulator not calibrated")
168
-
169
- # Get position and convert to unified space.
170
- manipulator_pos = self._get_pos(manipulator_id)
171
-
172
- # Shortcut return for Pathfinder.
173
- if self.num_axes == -1:
174
- return manipulator_pos
175
-
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
- )
180
- except KeyError:
181
- # Manipulator not found in registered manipulators.
182
- print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}")
183
- return PositionalResponse(error="Manipulator not registered")
184
-
185
- def get_angles(self, manipulator_id: str) -> AngularResponse:
186
- """Get the current position of a manipulator.
187
-
188
- :param manipulator_id: The ID of the manipulator to get the angles of.
189
- :type manipulator_id: str
190
- :return: Angular information for the manipulator and error message (if any).
191
- :rtype: :class:`vbl_aquarium.models.ephys_link.AngularResponse`
192
- """
193
- try:
194
- # Check calibration status
195
- if (
196
- hasattr(self.manipulators[manipulator_id], "get_calibrated")
197
- and not self.manipulators[manipulator_id].get_calibrated()
198
- ):
199
- print(f"[ERROR]\t\t Calibration not complete: {manipulator_id}\n")
200
- return AngularResponse(error="Manipulator not calibrated")
201
-
202
- # Get position
203
- return self._get_angles(manipulator_id)
204
-
205
- except KeyError:
206
- # Manipulator not found in registered manipulators
207
- print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}")
208
- return AngularResponse(error="Manipulator not registered")
209
-
210
- def get_shank_count(self, manipulator_id: str) -> ShankCountResponse:
211
- """Get the number of shanks on the probe
212
-
213
- :param manipulator_id: The ID of the manipulator to get the number of shanks of.
214
- :type manipulator_id: str
215
- :return: Number of shanks on the probe.
216
- :rtype: :class:`vbl_aquarium.models.ephys_link.ShankCountResponse`
217
- """
218
- return self._get_shank_count(manipulator_id)
219
-
220
- async def goto_pos(self, request: GotoPositionRequest) -> PositionalResponse:
221
- """Move manipulator to position
222
-
223
- :param request: The goto request parsed from the server.
224
- :type request: :class:`vbl_aquarium.models.ephys_link.GotoPositionRequest`
225
- :return: Resulting position of the manipulator and error message (if any).
226
- :rtype: :class:`vbl_aquarium.models.ephys_link.PositionalResponse`
227
- """
228
- try:
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")
233
-
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")
238
-
239
- # Convert position to platform space, move, and convert final position back to
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
- )
247
- except KeyError:
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")
251
-
252
- async def drive_to_depth(self, request: DriveToDepthRequest) -> DriveToDepthResponse:
253
- """Drive manipulator to depth
254
-
255
- :param request: The drive to depth request parsed from the server.
256
- :type request: :class:`vbl_aquarium.models.ephys_link.DriveToDepthRequest`
257
- :return: Resulting depth of the manipulator and error message (if any).
258
- :rtype: :class:`ephys_link.common.DriveToDepthOutputData`
259
- """
260
- try:
261
- # Check calibration status
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")
265
-
266
- # Check write state
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")
270
-
271
- end_depth = await self._drive_to_depth(
272
- request.model_copy(update={"depth": self._unified_space_to_platform_space(Vector4(w=request.depth)).w})
273
- )
274
- return end_depth.model_copy(
275
- update={"depth": self._platform_space_to_unified_space(Vector4(w=end_depth.depth)).w}
276
- )
277
- except KeyError:
278
- # Manipulator not found in registered manipulators
279
- print(f"[ERROR]\t\t Manipulator not registered: {request.manipulator_id}\n")
280
- return DriveToDepthResponse(error="Manipulator not registered")
281
-
282
- def set_inside_brain(self, request: InsideBrainRequest) -> BooleanStateResponse:
283
- """Set manipulator inside brain state (restricts motion)
284
-
285
- :param request: The inside brain request parsed from the server.
286
- :type request: :class:`vbl_aquarium.models.ephys_link.InsideBrainRequest`
287
- :return: New inside brain state of the manipulator and error message (if any).
288
- :rtype: :class:`vbl_aquarium.models.ephys_link.BooleanStateResponse`
289
- """
290
- try:
291
- # Check calibration status
292
- if (
293
- hasattr(self.manipulators[request.manipulator_id], "get_calibrated")
294
- and not self.manipulators[request.manipulator_id].get_calibrated()
295
- ):
296
- print("[ERROR]\t\t Calibration not complete\n")
297
- return BooleanStateResponse(error="Manipulator not calibrated")
298
-
299
- return self._set_inside_brain(request)
300
-
301
- except KeyError:
302
- # Manipulator not found in registered manipulators
303
- print(f"[ERROR]\t\t Manipulator {request.manipulator_id} not registered\n")
304
- return BooleanStateResponse(error="Manipulator not " "registered")
305
-
306
- except Exception as e:
307
- # Other error
308
- print(f"[ERROR]\t\t Set manipulator {request.manipulator_id} inside brain " f"state")
309
- print(f"{e}\n")
310
- return BooleanStateResponse(error="Error setting inside brain")
311
-
312
- async def calibrate(self, manipulator_id: str, sio: AsyncClient | AsyncServer) -> str:
313
- """Calibrate manipulator
314
-
315
- :param manipulator_id: ID of manipulator to calibrate
316
- :type manipulator_id: str
317
- :param sio: SocketIO object (to call sleep)
318
- :type sio: :class:`socketio.AsyncServer`
319
- :return: Error message on error, empty string otherwise.
320
- :rtype: str
321
- """
322
- try:
323
- # Check write state
324
- if not self.manipulators[manipulator_id].get_can_write():
325
- print(f"[ERROR]\t\t Cannot write to manipulator: {manipulator_id}")
326
- return "Cannot write to manipulator"
327
-
328
- return await self._calibrate(manipulator_id, sio)
329
-
330
- except KeyError:
331
- # Manipulator not found in registered manipulators
332
- print(f"[ERROR]\t\t Manipulator {manipulator_id} not registered\n")
333
- return "Manipulator not registered"
334
-
335
- except Exception as e:
336
- # Other error
337
- print(f"[ERROR]\t\t Calibrate manipulator {manipulator_id}")
338
- print(f"{e}\n")
339
- return "Error calibrating manipulator"
340
-
341
- def bypass_calibration(self, manipulator_id: str) -> str:
342
- """Bypass calibration of manipulator
343
-
344
- :param manipulator_id: ID of manipulator to bypass calibration
345
- :type manipulator_id: str
346
- :return: Error message on error, empty string otherwise.
347
- :rtype: str
348
- """
349
- try:
350
- # Bypass calibration
351
- return self._bypass_calibration(manipulator_id)
352
-
353
- except KeyError:
354
- # Manipulator not found in registered manipulators
355
- print(f"[ERROR]\t\t Manipulator {manipulator_id} not registered\n")
356
- return "Manipulator not registered"
357
-
358
- except Exception as e:
359
- # Other error
360
- print(f"[ERROR]\t\t Bypass calibration of manipulator {manipulator_id}")
361
- print(f"{e}\n")
362
- return "Error bypassing calibration"
363
-
364
- def set_can_write(
365
- self,
366
- request: CanWriteRequest,
367
- ) -> BooleanStateResponse:
368
- """Set manipulator can_write state (enables/disabled moving manipulator)
369
-
370
- :param request: The can write request parsed from the server.
371
- :type request: :class:`vbl_aquarium.models.ephys_link.CanWriteRequest`
372
- :return: New can_write state of the manipulator and error message (if any).
373
- :rtype: :class:`ephys_link.common.StateOutputData`
374
- """
375
- try:
376
- return self._set_can_write(request)
377
- except KeyError:
378
- # Manipulator not found in registered manipulators
379
- print(f"[ERROR]\t\t Manipulator not registered: {request.manipulator_id}\n")
380
- return BooleanStateResponse(error="Manipulator not registered")
381
- except Exception as e:
382
- # Other error
383
- print(f"[ERROR]\t\t Set manipulator {request.manipulator_id} can_write state")
384
- print(f"{e}\n")
385
- return BooleanStateResponse(error="Error setting can_write")
386
-
387
- # Platform specific methods to override
388
-
389
- @abstractmethod
390
- def _get_manipulators(self) -> list:
391
- raise NotImplementedError
392
-
393
- @abstractmethod
394
- def _register_manipulator(self, manipulator_id: str) -> None:
395
- raise NotImplementedError
396
-
397
- @abstractmethod
398
- def _unregister_manipulator(self, manipulator_id: str) -> None:
399
- raise NotImplementedError
400
-
401
- @abstractmethod
402
- def _get_pos(self, manipulator_id: str) -> PositionalResponse:
403
- raise NotImplementedError
404
-
405
- @abstractmethod
406
- def _get_angles(self, manipulator_id: str) -> AngularResponse:
407
- raise NotImplementedError
408
-
409
- @abstractmethod
410
- def _get_shank_count(self, manipulator_id: str) -> ShankCountResponse:
411
- raise NotImplementedError
412
-
413
- @abstractmethod
414
- async def _goto_pos(self, request: GotoPositionRequest) -> PositionalResponse:
415
- raise NotImplementedError
416
-
417
- @abstractmethod
418
- async def _drive_to_depth(self, request: DriveToDepthRequest) -> DriveToDepthResponse:
419
- raise NotImplementedError
420
-
421
- @abstractmethod
422
- def _set_inside_brain(self, request: InsideBrainRequest) -> BooleanStateResponse:
423
- raise NotImplementedError
424
-
425
- @abstractmethod
426
- async def _calibrate(self, manipulator_id: str, sio: AsyncClient | AsyncServer) -> str:
427
- """Calibrate manipulator
428
-
429
- :param manipulator_id: ID of manipulator to calibrate
430
- :type manipulator_id: str
431
- :param sio: SocketIO object (to call sleep)
432
- :type sio: :class:`socketio.AsyncServer`
433
- :return: Callback parameters (manipulator ID, error message)
434
- :rtype: str
435
- """
436
-
437
- @abstractmethod
438
- def _bypass_calibration(self, manipulator_id: str) -> str:
439
- raise NotImplementedError
440
-
441
- @abstractmethod
442
- def _set_can_write(self, request: CanWriteRequest) -> BooleanStateResponse:
443
- raise NotImplementedError
444
-
445
- @abstractmethod
446
- def _platform_space_to_unified_space(self, platform_position: Vector4) -> Vector4:
447
- """Convert position in platform space to position in unified manipulator space
448
-
449
- :param platform_position: Position in platform space (x, y, z, w) in mm
450
- :type platform_position: Vector4
451
- :return: Position in unified manipulator space (x, y, z, w) in mm
452
- :rtype: Vector4
453
- """
454
- raise NotImplementedError
455
-
456
- @abstractmethod
457
- def _unified_space_to_platform_space(self, unified_position: Vector4) -> Vector4:
458
- """Convert position in unified manipulator space to position in platform space
459
-
460
- :param unified_position: Position in unified manipulator space (x, y, z, w) in mm
461
- :type unified_position: Vector4
462
- :return: Position in platform space (x, y, z, w) in mm
463
- :rtype: Vector4
464
- """
465
- raise NotImplementedError
@@ -1,35 +0,0 @@
1
- """Defines what the properties and required functionality of a manipulator are.
2
-
3
- Most functionality will be implemented on the platform handler side. This is mostly
4
- for enforcing implementation of the stop method and hold common properties.
5
- """
6
-
7
- from abc import ABC, abstractmethod
8
-
9
- # Constants
10
- MM_TO_UM = 1000
11
- HOURS_TO_SECONDS = 3600
12
- POSITION_POLL_DELAY = 0.1
13
-
14
-
15
- class PlatformManipulator(ABC):
16
- """An abstract class that defines the interface for a manipulator."""
17
-
18
- def __init__(self):
19
- """Initialize manipulator."""
20
-
21
- self._id = None
22
- self._movement_tolerance = 0.001
23
- self._calibrated = False
24
- self._inside_brain = False
25
- self._can_write = False
26
- self._reset_timer = None
27
- self._is_moving = False
28
-
29
- @abstractmethod
30
- def stop(self) -> None:
31
- """Stop all axes on manipulator
32
-
33
- :returns None
34
- """
35
- raise NotImplementedError
@@ -1,5 +0,0 @@
1
- import clr
2
-
3
- # Load New Scale API
4
- # noinspection PyUnresolvedReferences
5
- clr.AddReference("ephys_link/resources/NstMotorCtrl")
@@ -1,141 +0,0 @@
1
- """Handle communications with New Scale API
2
-
3
- Implements New Scale specific API calls.
4
-
5
- This is a subclass of :class:`ephys_link.platform_handler.PlatformHandler`.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- from typing import TYPE_CHECKING
11
-
12
- # noinspection PyUnresolvedReferences
13
- from NstMotorCtrl import NstCtrlHostIntf
14
- from vbl_aquarium.models.ephys_link import (
15
- AngularResponse,
16
- BooleanStateResponse,
17
- CanWriteRequest,
18
- DriveToDepthRequest,
19
- DriveToDepthResponse,
20
- GotoPositionRequest,
21
- InsideBrainRequest,
22
- PositionalResponse,
23
- ShankCountResponse,
24
- )
25
- from vbl_aquarium.models.unity import Vector4
26
-
27
- from ephys_link import common as com
28
- from ephys_link.platform_handler import PlatformHandler
29
- from ephys_link.platforms.new_scale_manipulator import NewScaleManipulator
30
-
31
- if TYPE_CHECKING:
32
- import socketio
33
-
34
-
35
- class NewScaleHandler(PlatformHandler):
36
- """Handler for New Scale platform"""
37
-
38
- def __init__(self) -> None:
39
- """Initialize New Scale handler"""
40
- super().__init__()
41
-
42
- self.num_axes = 3
43
- self.dimensions = Vector4(x=15, y=15, z=15, w=0)
44
-
45
- self.ctrl = NstCtrlHostIntf()
46
-
47
- # Connect manipulators and initialize
48
- self.ctrl.ShowProperties()
49
- self.ctrl.Initialize()
50
-
51
- def _get_manipulators(self) -> list:
52
- return list(map(str, range(self.ctrl.PortCount)))
53
-
54
- def _register_manipulator(self, manipulator_id: str) -> None:
55
- # Check if ID is numeric
56
- if not manipulator_id.isnumeric():
57
- msg = "Manipulator ID must be numeric"
58
- raise ValueError(msg)
59
-
60
- # Check if ID is connected
61
- if manipulator_id not in self._get_manipulators():
62
- msg = f"Manipulator {manipulator_id} not connected"
63
- raise ValueError(msg)
64
-
65
- # Check if there are enough axes
66
- if int(manipulator_id) * 3 + 2 >= self.ctrl.AxisCount:
67
- msg = f"Manipulator {manipulator_id} has no axes"
68
- raise ValueError(msg)
69
-
70
- # Register manipulator
71
- first_axis_index = int(manipulator_id) * 3
72
- self.manipulators[manipulator_id] = NewScaleManipulator(
73
- manipulator_id,
74
- self.ctrl.GetAxis(first_axis_index),
75
- self.ctrl.GetAxis(first_axis_index + 1),
76
- self.ctrl.GetAxis(first_axis_index + 2),
77
- )
78
-
79
- def _unregister_manipulator(self, manipulator_id: str) -> None:
80
- del self.manipulators[manipulator_id]
81
-
82
- def _get_pos(self, manipulator_id: str) -> PositionalResponse:
83
- return self.manipulators[manipulator_id].get_pos()
84
-
85
- def _get_angles(self, manipulator_id: str) -> AngularResponse:
86
- raise NotImplementedError
87
-
88
- def _get_shank_count(self, manipulator_id: str) -> ShankCountResponse:
89
- raise NotImplementedError
90
-
91
- async def _goto_pos(self, request: GotoPositionRequest) -> PositionalResponse:
92
- return await self.manipulators[request.manipulator_id].goto_pos(request)
93
-
94
- async def _drive_to_depth(self, request: DriveToDepthRequest) -> DriveToDepthResponse:
95
- return await self.manipulators[request.manipulator_id].drive_to_depth(request)
96
-
97
- def _set_inside_brain(self, request: InsideBrainRequest) -> BooleanStateResponse:
98
- self.manipulators[request.manipulator_id].set_inside_brain(request.inside)
99
- com.dprint(f"[SUCCESS]\t Set inside brain state for manipulator: {request.manipulator_id}\n")
100
- return BooleanStateResponse(state=request.inside)
101
-
102
- async def _calibrate(self, manipulator_id: str, sio: socketio.AsyncServer) -> str:
103
- return "" if self.manipulators[manipulator_id].calibrate() else "Error calling calibrate"
104
-
105
- def _bypass_calibration(self, manipulator_id: str) -> str:
106
- self.manipulators[manipulator_id].set_calibrated()
107
- com.dprint(f"[SUCCESS]\t Bypassed calibration for manipulator" f" {manipulator_id}\n")
108
- return ""
109
-
110
- def _set_can_write(self, request: CanWriteRequest) -> BooleanStateResponse:
111
- self.manipulators[request.manipulator_id].set_can_write(request)
112
- com.dprint(f"[SUCCESS]\t Set can_write state for manipulator {request.manipulator_id}\n")
113
- return BooleanStateResponse(state=request.can_write)
114
-
115
- def _platform_space_to_unified_space(self, platform_position: Vector4) -> Vector4:
116
- # unified <- platform
117
- # +x <- -x
118
- # +y <- +z
119
- # +z <- +y
120
- # +d <- -d
121
-
122
- return Vector4(
123
- x=self.dimensions.x - platform_position.x,
124
- y=platform_position.z,
125
- z=platform_position.y,
126
- w=self.dimensions.z - platform_position.w,
127
- )
128
-
129
- def _unified_space_to_platform_space(self, unified_position: Vector4) -> Vector4:
130
- # platform <- unified
131
- # +x <- -x
132
- # +y <- +z
133
- # +z <- +y
134
- # +d <- -d
135
-
136
- return Vector4(
137
- x=self.dimensions.x - unified_position.x,
138
- y=unified_position.z,
139
- z=unified_position.y,
140
- w=self.dimensions.z - unified_position.w,
141
- )