ephys-link 1.0.5__py3-none-any.whl → 1.0.6a0__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 -0
- ephys_link/__main__.py +3 -6
- ephys_link/common.py +9 -20
- ephys_link/gui.py +20 -37
- ephys_link/platform_handler.py +26 -48
- ephys_link/platforms/__init__.py +0 -0
- ephys_link/platforms/new_scale_handler.py +21 -36
- ephys_link/platforms/new_scale_manipulator.py +16 -31
- ephys_link/platforms/new_scale_pathfinder_handler.py +24 -32
- ephys_link/platforms/sensapex_handler.py +23 -39
- ephys_link/platforms/sensapex_manipulator.py +16 -26
- ephys_link/platforms/ump3_handler.py +23 -38
- ephys_link/platforms/ump3_manipulator.py +16 -26
- ephys_link/server.py +16 -30
- ephys_link-1.0.6a0.dist-info/METADATA +158 -0
- ephys_link-1.0.6a0.dist-info/RECORD +25 -0
- {ephys_link-1.0.5.dist-info → ephys_link-1.0.6a0.dist-info}/WHEEL +1 -2
- ephys_link-1.0.5.dist-info/METADATA +0 -828
- ephys_link-1.0.5.dist-info/RECORD +0 -24
- ephys_link-1.0.5.dist-info/top_level.txt +0 -1
- {ephys_link-1.0.5.dist-info → ephys_link-1.0.6a0.dist-info}/entry_points.txt +0 -0
- {ephys_link-1.0.5.dist-info → ephys_link-1.0.6a0.dist-info/licenses}/LICENSE +0 -0
ephys_link/__about__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.6a0"
|
ephys_link/__main__.py
CHANGED
|
@@ -69,12 +69,9 @@ parser.add_argument(
|
|
|
69
69
|
type=str,
|
|
70
70
|
dest="type",
|
|
71
71
|
default="sensapex",
|
|
72
|
-
help='Manipulator type (i.e. "sensapex", "new_scale", or "new_scale_pathfinder").'
|
|
73
|
-
' Default: "sensapex"',
|
|
74
|
-
)
|
|
75
|
-
parser.add_argument(
|
|
76
|
-
"-d", "--debug", dest="debug", action="store_true", help="Enable debug mode"
|
|
72
|
+
help='Manipulator type (i.e. "sensapex", "new_scale", or "new_scale_pathfinder").' ' Default: "sensapex"',
|
|
77
73
|
)
|
|
74
|
+
parser.add_argument("-d", "--debug", dest="debug", action="store_true", help="Enable debug mode")
|
|
78
75
|
parser.add_argument(
|
|
79
76
|
"-p",
|
|
80
77
|
"--port",
|
|
@@ -113,7 +110,7 @@ def main() -> None:
|
|
|
113
110
|
|
|
114
111
|
# Parse arguments
|
|
115
112
|
args = parser.parse_args()
|
|
116
|
-
com.
|
|
113
|
+
com.DEBUG = args.debug
|
|
117
114
|
|
|
118
115
|
# Setup serial port
|
|
119
116
|
if args.serial != "no-e-stop":
|
ephys_link/common.py
CHANGED
|
@@ -4,23 +4,14 @@ Contains globally used helper functions and typed dictionaries (to be used as
|
|
|
4
4
|
callback parameters)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
7
9
|
from typing import TypedDict
|
|
8
10
|
|
|
9
11
|
# Debugging flag
|
|
10
12
|
DEBUG = False
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
def set_debug(debug: bool) -> None:
|
|
14
|
-
"""Set debug flag
|
|
15
|
-
|
|
16
|
-
:param debug: True to enable debug mode, False to disable
|
|
17
|
-
:type debug: bool
|
|
18
|
-
:return: None
|
|
19
|
-
"""
|
|
20
|
-
global DEBUG
|
|
21
|
-
DEBUG = debug
|
|
22
|
-
|
|
23
|
-
|
|
24
15
|
def dprint(message: str) -> None:
|
|
25
16
|
"""Print message if debug is enabled
|
|
26
17
|
|
|
@@ -81,11 +72,9 @@ class GetManipulatorsOutputData(dict):
|
|
|
81
72
|
:code:`{"manipulators": ["1", "2"], "error": ""}`
|
|
82
73
|
"""
|
|
83
74
|
|
|
84
|
-
def __init__(
|
|
85
|
-
self, manipulators: list, num_axes: int, dimensions: list, error: str
|
|
86
|
-
) -> None:
|
|
75
|
+
def __init__(self, manipulators: list, num_axes: int, dimensions: list, error: str) -> None:
|
|
87
76
|
"""Constructor"""
|
|
88
|
-
super(
|
|
77
|
+
super().__init__(
|
|
89
78
|
manipulators=manipulators,
|
|
90
79
|
num_axes=num_axes,
|
|
91
80
|
dimensions=dimensions,
|
|
@@ -107,7 +96,7 @@ class PositionalOutputData(dict):
|
|
|
107
96
|
|
|
108
97
|
def __init__(self, position: list, error: str) -> None:
|
|
109
98
|
"""Constructor"""
|
|
110
|
-
super(
|
|
99
|
+
super().__init__(position=position, error=error)
|
|
111
100
|
|
|
112
101
|
|
|
113
102
|
class AngularOutputData(dict):
|
|
@@ -121,7 +110,7 @@ class AngularOutputData(dict):
|
|
|
121
110
|
|
|
122
111
|
def __init__(self, angles: list, error: str) -> None:
|
|
123
112
|
"""Constructor"""
|
|
124
|
-
super(
|
|
113
|
+
super().__init__(angles=angles, error=error)
|
|
125
114
|
|
|
126
115
|
|
|
127
116
|
class ShankCountOutputData(dict):
|
|
@@ -135,7 +124,7 @@ class ShankCountOutputData(dict):
|
|
|
135
124
|
|
|
136
125
|
def __init__(self, shank_count: int, error: str) -> None:
|
|
137
126
|
"""Constructor"""
|
|
138
|
-
super(
|
|
127
|
+
super().__init__(shank_count=shank_count, error=error)
|
|
139
128
|
|
|
140
129
|
|
|
141
130
|
class DriveToDepthOutputData(dict):
|
|
@@ -151,7 +140,7 @@ class DriveToDepthOutputData(dict):
|
|
|
151
140
|
|
|
152
141
|
def __init__(self, depth: float, error: str) -> None:
|
|
153
142
|
"""Create drive to depth output data dictionary"""
|
|
154
|
-
super(
|
|
143
|
+
super().__init__(depth=depth, error=error)
|
|
155
144
|
|
|
156
145
|
|
|
157
146
|
class StateOutputData(dict):
|
|
@@ -167,4 +156,4 @@ class StateOutputData(dict):
|
|
|
167
156
|
|
|
168
157
|
def __init__(self, state: bool, error: str) -> None:
|
|
169
158
|
"""Create state output data dictionary"""
|
|
170
|
-
super(
|
|
159
|
+
super().__init__(state=state, error=error)
|
ephys_link/gui.py
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import socket
|
|
3
3
|
from argparse import Namespace
|
|
4
4
|
from threading import Event, Thread
|
|
5
|
-
from tkinter import
|
|
6
|
-
from tkinter import ttk
|
|
5
|
+
from tkinter import CENTER, RIGHT, E, IntVar, StringVar, Tk, ttk
|
|
7
6
|
|
|
8
7
|
# GUI Variables
|
|
9
8
|
is_running = False
|
|
@@ -65,33 +64,25 @@ class GUI:
|
|
|
65
64
|
|
|
66
65
|
# Server serving settings
|
|
67
66
|
|
|
68
|
-
server_serving_settings = ttk.LabelFrame(
|
|
69
|
-
mainframe, text="Serving settings", padding=3
|
|
70
|
-
)
|
|
67
|
+
server_serving_settings = ttk.LabelFrame(mainframe, text="Serving settings", padding=3)
|
|
71
68
|
server_serving_settings.grid(column=0, row=0, sticky="news")
|
|
72
69
|
|
|
73
70
|
# IP
|
|
74
|
-
ttk.Label(server_serving_settings, text="IP:", anchor=E, justify=RIGHT).grid(
|
|
75
|
-
|
|
71
|
+
ttk.Label(server_serving_settings, text="IP:", anchor=E, justify=RIGHT).grid(column=0, row=0, sticky="we")
|
|
72
|
+
ttk.Label(server_serving_settings, text=socket.gethostbyname(socket.gethostname())).grid(
|
|
73
|
+
column=1, row=0, sticky="we"
|
|
76
74
|
)
|
|
77
|
-
ttk.Label(
|
|
78
|
-
server_serving_settings, text=socket.gethostbyname(socket.gethostname())
|
|
79
|
-
).grid(column=1, row=0, sticky="we")
|
|
80
75
|
|
|
81
76
|
# Port
|
|
82
|
-
ttk.Label(server_serving_settings, text="Port:", anchor=E, justify=RIGHT).grid(
|
|
83
|
-
|
|
77
|
+
ttk.Label(server_serving_settings, text="Port:", anchor=E, justify=RIGHT).grid(column=0, row=1, sticky="we")
|
|
78
|
+
ttk.Entry(server_serving_settings, textvariable=server_port, width=5, justify=CENTER).grid(
|
|
79
|
+
column=1, row=1, sticky="we"
|
|
84
80
|
)
|
|
85
|
-
ttk.Entry(
|
|
86
|
-
server_serving_settings, textvariable=server_port, width=5, justify=CENTER
|
|
87
|
-
).grid(column=1, row=1, sticky="we")
|
|
88
81
|
|
|
89
82
|
# ---
|
|
90
83
|
|
|
91
84
|
# Platform type
|
|
92
|
-
platform_type_settings = ttk.LabelFrame(
|
|
93
|
-
mainframe, text="Platform Type", padding=3
|
|
94
|
-
)
|
|
85
|
+
platform_type_settings = ttk.LabelFrame(mainframe, text="Platform Type", padding=3)
|
|
95
86
|
platform_type_settings.grid(column=0, row=1, sticky="news")
|
|
96
87
|
|
|
97
88
|
ttk.Radiobutton(
|
|
@@ -110,34 +101,26 @@ class GUI:
|
|
|
110
101
|
# ---
|
|
111
102
|
|
|
112
103
|
# New Scale Settings
|
|
113
|
-
new_scale_settings = ttk.LabelFrame(
|
|
114
|
-
mainframe, text="New Scale settings", padding=3
|
|
115
|
-
)
|
|
104
|
+
new_scale_settings = ttk.LabelFrame(mainframe, text="New Scale settings", padding=3)
|
|
116
105
|
new_scale_settings.grid(column=0, row=2, sticky="news")
|
|
117
106
|
|
|
118
107
|
# Port
|
|
119
|
-
ttk.Label(
|
|
120
|
-
|
|
121
|
-
)
|
|
122
|
-
ttk.Entry(
|
|
123
|
-
|
|
124
|
-
)
|
|
108
|
+
ttk.Label(new_scale_settings, text="HTTP Server Port:", anchor=E, justify=RIGHT).grid(
|
|
109
|
+
column=0, row=1, sticky="we"
|
|
110
|
+
)
|
|
111
|
+
ttk.Entry(new_scale_settings, textvariable=new_scale_port, width=5, justify=CENTER).grid(
|
|
112
|
+
column=1, row=1, sticky="we"
|
|
113
|
+
)
|
|
125
114
|
|
|
126
115
|
# ---
|
|
127
116
|
|
|
128
117
|
# Emergency Stop serial port
|
|
129
|
-
e_stop_settings = ttk.LabelFrame(
|
|
130
|
-
mainframe, text="Emergency Stop Settings", padding=3
|
|
131
|
-
)
|
|
118
|
+
e_stop_settings = ttk.LabelFrame(mainframe, text="Emergency Stop Settings", padding=3)
|
|
132
119
|
e_stop_settings.grid(column=0, row=3, sticky="news")
|
|
133
120
|
|
|
134
121
|
# Serial Port
|
|
135
|
-
ttk.Label(e_stop_settings, text="Serial Port:", anchor=E, justify=RIGHT).grid(
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
ttk.Entry(
|
|
139
|
-
e_stop_settings, textvariable=e_stop_serial_port, justify=CENTER
|
|
140
|
-
).grid(column=1, row=1, sticky="we")
|
|
122
|
+
ttk.Label(e_stop_settings, text="Serial Port:", anchor=E, justify=RIGHT).grid(column=0, row=1, sticky="we")
|
|
123
|
+
ttk.Entry(e_stop_settings, textvariable=e_stop_serial_port, justify=CENTER).grid(column=1, row=1, sticky="we")
|
|
141
124
|
|
|
142
125
|
# Server start/stop button
|
|
143
126
|
ttk.Button(
|
|
@@ -153,7 +136,7 @@ class GUI:
|
|
|
153
136
|
:type start: bool
|
|
154
137
|
:return None
|
|
155
138
|
"""
|
|
156
|
-
global
|
|
139
|
+
global is_running
|
|
157
140
|
is_running = not is_running
|
|
158
141
|
if start:
|
|
159
142
|
# Launch serial
|
ephys_link/platform_handler.py
CHANGED
|
@@ -12,13 +12,16 @@ server receives an event from a client. In general, each function does the follo
|
|
|
12
12
|
4. Return the callback parameters to :mod:`ephys_link.server`
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from
|
|
15
|
+
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
import
|
|
17
|
+
from abc import ABC, abstractmethod
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
19
|
|
|
20
20
|
from ephys_link import common as com
|
|
21
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
import socketio
|
|
24
|
+
|
|
22
25
|
|
|
23
26
|
class PlatformHandler(ABC):
|
|
24
27
|
"""An abstract class that defines the interface for a manipulator handler."""
|
|
@@ -56,10 +59,11 @@ class PlatformHandler(ABC):
|
|
|
56
59
|
for manipulator in self.manipulators.values():
|
|
57
60
|
if hasattr(manipulator, "stop"):
|
|
58
61
|
manipulator.stop()
|
|
59
|
-
return True
|
|
60
62
|
except Exception as e:
|
|
61
63
|
print(f"[ERROR]\t\t Could not stop manipulators: {e}\n")
|
|
62
64
|
return False
|
|
65
|
+
else:
|
|
66
|
+
return True
|
|
63
67
|
|
|
64
68
|
def get_manipulators(self) -> com.GetManipulatorsOutputData:
|
|
65
69
|
"""Get all registered manipulators
|
|
@@ -75,10 +79,8 @@ class PlatformHandler(ABC):
|
|
|
75
79
|
error = ""
|
|
76
80
|
except Exception as e:
|
|
77
81
|
print(f"[ERROR]\t\t Getting manipulators: {type(e)}: {e}\n")
|
|
78
|
-
|
|
79
|
-
return com.GetManipulatorsOutputData(
|
|
80
|
-
devices, self.num_axes, self.dimensions, error
|
|
81
|
-
)
|
|
82
|
+
else:
|
|
83
|
+
return com.GetManipulatorsOutputData(devices, self.num_axes, self.dimensions, error)
|
|
82
84
|
|
|
83
85
|
def register_manipulator(self, manipulator_id: str) -> str:
|
|
84
86
|
"""Register a manipulator
|
|
@@ -97,18 +99,17 @@ class PlatformHandler(ABC):
|
|
|
97
99
|
# Register manipulator
|
|
98
100
|
self._register_manipulator(manipulator_id)
|
|
99
101
|
com.dprint(f"[SUCCESS]\t Registered manipulator: {manipulator_id}\n")
|
|
100
|
-
return ""
|
|
101
|
-
|
|
102
102
|
except ValueError as ve:
|
|
103
103
|
# Manipulator not found in UMP
|
|
104
104
|
print(f"[ERROR]\t\t Manipulator not found: {manipulator_id}: {ve}\n")
|
|
105
105
|
return "Manipulator not found"
|
|
106
|
-
|
|
107
106
|
except Exception as e:
|
|
108
107
|
# Other error
|
|
109
108
|
print(f"[ERROR]\t\t Registering manipulator: {manipulator_id}")
|
|
110
109
|
print(f"{type(e)}: {e}\n")
|
|
111
110
|
return "Error registering manipulator"
|
|
111
|
+
else:
|
|
112
|
+
return ""
|
|
112
113
|
|
|
113
114
|
def unregister_manipulator(self, manipulator_id: str) -> str:
|
|
114
115
|
"""Unregister a manipulator
|
|
@@ -127,12 +128,13 @@ class PlatformHandler(ABC):
|
|
|
127
128
|
self._unregister_manipulator(manipulator_id)
|
|
128
129
|
|
|
129
130
|
com.dprint(f"[SUCCESS]\t Unregistered manipulator: {manipulator_id}\n")
|
|
130
|
-
return ""
|
|
131
131
|
except Exception as e:
|
|
132
132
|
# Other error
|
|
133
133
|
print(f"[ERROR]\t\t Unregistering manipulator: {manipulator_id}")
|
|
134
134
|
print(f"{e}\n")
|
|
135
135
|
return "Error unregistering manipulator"
|
|
136
|
+
else:
|
|
137
|
+
return ""
|
|
136
138
|
|
|
137
139
|
def get_pos(self, manipulator_id: str) -> com.PositionalOutputData:
|
|
138
140
|
"""Get the current position of a manipulator
|
|
@@ -162,9 +164,7 @@ class PlatformHandler(ABC):
|
|
|
162
164
|
# Error check and convert position to unified space
|
|
163
165
|
if manipulator_pos["error"] != "":
|
|
164
166
|
return manipulator_pos
|
|
165
|
-
return com.PositionalOutputData(
|
|
166
|
-
self._platform_space_to_unified_space(manipulator_pos["position"]), ""
|
|
167
|
-
)
|
|
167
|
+
return com.PositionalOutputData(self._platform_space_to_unified_space(manipulator_pos["position"]), "")
|
|
168
168
|
|
|
169
169
|
except KeyError:
|
|
170
170
|
# Manipulator not found in registered manipulators
|
|
@@ -206,9 +206,7 @@ class PlatformHandler(ABC):
|
|
|
206
206
|
"""
|
|
207
207
|
return self._get_shank_count(manipulator_id)
|
|
208
208
|
|
|
209
|
-
async def goto_pos(
|
|
210
|
-
self, manipulator_id: str, position: list[float], speed: int
|
|
211
|
-
) -> com.PositionalOutputData:
|
|
209
|
+
async def goto_pos(self, manipulator_id: str, position: list[float], speed: int) -> com.PositionalOutputData:
|
|
212
210
|
"""Move manipulator to position
|
|
213
211
|
|
|
214
212
|
:param manipulator_id: The ID of the manipulator to move
|
|
@@ -234,23 +232,17 @@ class PlatformHandler(ABC):
|
|
|
234
232
|
|
|
235
233
|
# Convert position to platform space, move, and convert final position back to
|
|
236
234
|
# unified space
|
|
237
|
-
end_position = await self._goto_pos(
|
|
238
|
-
manipulator_id, self._unified_space_to_platform_space(position), speed
|
|
239
|
-
)
|
|
235
|
+
end_position = await self._goto_pos(manipulator_id, self._unified_space_to_platform_space(position), speed)
|
|
240
236
|
if end_position["error"] != "":
|
|
241
237
|
return end_position
|
|
242
|
-
return com.PositionalOutputData(
|
|
243
|
-
self._platform_space_to_unified_space(end_position["position"]), ""
|
|
244
|
-
)
|
|
238
|
+
return com.PositionalOutputData(self._platform_space_to_unified_space(end_position["position"]), "")
|
|
245
239
|
|
|
246
240
|
except KeyError:
|
|
247
241
|
# Manipulator not found in registered manipulators
|
|
248
242
|
print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}\n")
|
|
249
243
|
return com.PositionalOutputData([], "Manipulator not registered")
|
|
250
244
|
|
|
251
|
-
async def drive_to_depth(
|
|
252
|
-
self, manipulator_id: str, depth: float, speed: int
|
|
253
|
-
) -> com.DriveToDepthOutputData:
|
|
245
|
+
async def drive_to_depth(self, manipulator_id: str, depth: float, speed: int) -> com.DriveToDepthOutputData:
|
|
254
246
|
"""Drive manipulator to depth
|
|
255
247
|
|
|
256
248
|
:param manipulator_id: The ID of the manipulator to drive
|
|
@@ -291,9 +283,7 @@ class PlatformHandler(ABC):
|
|
|
291
283
|
print(f"[ERROR]\t\t Manipulator not registered: {manipulator_id}\n")
|
|
292
284
|
return com.DriveToDepthOutputData(0, "Manipulator " "not registered")
|
|
293
285
|
|
|
294
|
-
def set_inside_brain(
|
|
295
|
-
self, manipulator_id: str, inside: bool
|
|
296
|
-
) -> com.StateOutputData:
|
|
286
|
+
def set_inside_brain(self, manipulator_id: str, inside: bool) -> com.StateOutputData:
|
|
297
287
|
"""Set manipulator inside brain state (restricts motion)
|
|
298
288
|
|
|
299
289
|
:param manipulator_id: The ID of the manipulator to set the state of
|
|
@@ -321,9 +311,7 @@ class PlatformHandler(ABC):
|
|
|
321
311
|
|
|
322
312
|
except Exception as e:
|
|
323
313
|
# Other error
|
|
324
|
-
print(
|
|
325
|
-
f"[ERROR]\t\t Set manipulator {manipulator_id} inside brain " f"state"
|
|
326
|
-
)
|
|
314
|
+
print(f"[ERROR]\t\t Set manipulator {manipulator_id} inside brain " f"state")
|
|
327
315
|
print(f"{e}\n")
|
|
328
316
|
return com.StateOutputData(False, "Error setting " "inside brain")
|
|
329
317
|
|
|
@@ -472,9 +460,7 @@ class PlatformHandler(ABC):
|
|
|
472
460
|
"""
|
|
473
461
|
|
|
474
462
|
@abstractmethod
|
|
475
|
-
async def _goto_pos(
|
|
476
|
-
self, manipulator_id: str, position: list[float], speed: int
|
|
477
|
-
) -> com.PositionalOutputData:
|
|
463
|
+
async def _goto_pos(self, manipulator_id: str, position: list[float], speed: int) -> com.PositionalOutputData:
|
|
478
464
|
"""Move manipulator to position
|
|
479
465
|
|
|
480
466
|
:param manipulator_id: The ID of the manipulator to move
|
|
@@ -489,9 +475,7 @@ class PlatformHandler(ABC):
|
|
|
489
475
|
"""
|
|
490
476
|
|
|
491
477
|
@abstractmethod
|
|
492
|
-
async def _drive_to_depth(
|
|
493
|
-
self, manipulator_id: str, depth: float, speed: int
|
|
494
|
-
) -> com.DriveToDepthOutputData:
|
|
478
|
+
async def _drive_to_depth(self, manipulator_id: str, depth: float, speed: int) -> com.DriveToDepthOutputData:
|
|
495
479
|
"""Drive manipulator to depth
|
|
496
480
|
|
|
497
481
|
:param manipulator_id: The ID of the manipulator to drive
|
|
@@ -506,9 +490,7 @@ class PlatformHandler(ABC):
|
|
|
506
490
|
"""
|
|
507
491
|
|
|
508
492
|
@abstractmethod
|
|
509
|
-
def _set_inside_brain(
|
|
510
|
-
self, manipulator_id: str, inside: bool
|
|
511
|
-
) -> com.StateOutputData:
|
|
493
|
+
def _set_inside_brain(self, manipulator_id: str, inside: bool) -> com.StateOutputData:
|
|
512
494
|
"""Set manipulator inside brain state (restricts motion)
|
|
513
495
|
|
|
514
496
|
:param manipulator_id: The ID of the manipulator to set the state of
|
|
@@ -564,9 +546,7 @@ class PlatformHandler(ABC):
|
|
|
564
546
|
"""
|
|
565
547
|
|
|
566
548
|
@abstractmethod
|
|
567
|
-
def _platform_space_to_unified_space(
|
|
568
|
-
self, platform_position: list[float]
|
|
569
|
-
) -> list[float]:
|
|
549
|
+
def _platform_space_to_unified_space(self, platform_position: list[float]) -> list[float]:
|
|
570
550
|
"""Convert position in platform space to position in unified manipulator space
|
|
571
551
|
|
|
572
552
|
:param platform_position: Position in platform space (x, y, z, w) in mm
|
|
@@ -576,9 +556,7 @@ class PlatformHandler(ABC):
|
|
|
576
556
|
"""
|
|
577
557
|
|
|
578
558
|
@abstractmethod
|
|
579
|
-
def _unified_space_to_platform_space(
|
|
580
|
-
self, unified_position: list[float]
|
|
581
|
-
) -> list[float]:
|
|
559
|
+
def _unified_space_to_platform_space(self, unified_position: list[float]) -> list[float]:
|
|
582
560
|
"""Convert position in unified manipulator space to position in platform space
|
|
583
561
|
|
|
584
562
|
:param unified_position: Position in unified manipulator space (x, y, z, w) in mm
|
|
File without changes
|
|
@@ -5,17 +5,20 @@ Implements New Scale specific API calls.
|
|
|
5
5
|
This is a subclass of :class:`ephys_link.platform_handler.PlatformHandler`.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
8
10
|
import importlib
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
9
12
|
|
|
10
13
|
# noinspection PyPackageRequirements
|
|
11
14
|
import clr
|
|
12
15
|
|
|
13
|
-
# noinspection PyPackageRequirements
|
|
14
|
-
import socketio
|
|
15
|
-
|
|
16
16
|
from ephys_link import common as com
|
|
17
17
|
from ephys_link.platform_handler import PlatformHandler
|
|
18
18
|
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
import socketio
|
|
21
|
+
|
|
19
22
|
|
|
20
23
|
class NewScaleHandler(PlatformHandler):
|
|
21
24
|
"""Handler for New Scale platform"""
|
|
@@ -45,15 +48,18 @@ class NewScaleHandler(PlatformHandler):
|
|
|
45
48
|
def _register_manipulator(self, manipulator_id: str) -> None:
|
|
46
49
|
# Check if ID is numeric
|
|
47
50
|
if not manipulator_id.isnumeric():
|
|
48
|
-
|
|
51
|
+
msg = "Manipulator ID must be numeric"
|
|
52
|
+
raise ValueError(msg)
|
|
49
53
|
|
|
50
54
|
# Check if ID is connected
|
|
51
55
|
if manipulator_id not in self._get_manipulators():
|
|
52
|
-
|
|
56
|
+
msg = f"Manipulator {manipulator_id} not connected"
|
|
57
|
+
raise ValueError(msg)
|
|
53
58
|
|
|
54
59
|
# Check if there are enough axes
|
|
55
60
|
if int(manipulator_id) * 3 + 2 >= self.ctrl.AxisCount:
|
|
56
|
-
|
|
61
|
+
msg = f"Manipulator {manipulator_id} has no axes"
|
|
62
|
+
raise ValueError(msg)
|
|
57
63
|
|
|
58
64
|
# Register manipulator
|
|
59
65
|
first_axis_index = int(manipulator_id) * 3
|
|
@@ -78,38 +84,23 @@ class NewScaleHandler(PlatformHandler):
|
|
|
78
84
|
def _get_shank_count(self, manipulator_id: str) -> com.ShankCountOutputData:
|
|
79
85
|
raise NotImplementedError
|
|
80
86
|
|
|
81
|
-
async def _goto_pos(
|
|
82
|
-
self, manipulator_id: str, position: list[float], speed: int
|
|
83
|
-
) -> com.PositionalOutputData:
|
|
87
|
+
async def _goto_pos(self, manipulator_id: str, position: list[float], speed: int) -> com.PositionalOutputData:
|
|
84
88
|
return await self.manipulators[manipulator_id].goto_pos(position, speed)
|
|
85
89
|
|
|
86
|
-
async def _drive_to_depth(
|
|
87
|
-
self, manipulator_id: str, depth: float, speed: int
|
|
88
|
-
) -> com.DriveToDepthOutputData:
|
|
90
|
+
async def _drive_to_depth(self, manipulator_id: str, depth: float, speed: int) -> com.DriveToDepthOutputData:
|
|
89
91
|
return await self.manipulators[manipulator_id].drive_to_depth(depth, speed)
|
|
90
92
|
|
|
91
|
-
def _set_inside_brain(
|
|
92
|
-
self, manipulator_id: str, inside: bool
|
|
93
|
-
) -> com.StateOutputData:
|
|
93
|
+
def _set_inside_brain(self, manipulator_id: str, inside: bool) -> com.StateOutputData:
|
|
94
94
|
self.manipulators[manipulator_id].set_inside_brain(inside)
|
|
95
|
-
com.dprint(
|
|
96
|
-
f"[SUCCESS]\t Set inside brain state for manipulator:"
|
|
97
|
-
f" {manipulator_id}\n"
|
|
98
|
-
)
|
|
95
|
+
com.dprint(f"[SUCCESS]\t Set inside brain state for manipulator:" f" {manipulator_id}\n")
|
|
99
96
|
return com.StateOutputData(inside, "")
|
|
100
97
|
|
|
101
98
|
async def _calibrate(self, manipulator_id: str, sio: socketio.AsyncServer) -> str:
|
|
102
|
-
return (
|
|
103
|
-
""
|
|
104
|
-
if self.manipulators[manipulator_id].calibrate()
|
|
105
|
-
else "Error calling calibrate"
|
|
106
|
-
)
|
|
99
|
+
return "" if self.manipulators[manipulator_id].calibrate() else "Error calling calibrate"
|
|
107
100
|
|
|
108
101
|
def _bypass_calibration(self, manipulator_id: str) -> str:
|
|
109
102
|
self.manipulators[manipulator_id].set_calibrated()
|
|
110
|
-
com.dprint(
|
|
111
|
-
f"[SUCCESS]\t Bypassed calibration for manipulator" f" {manipulator_id}\n"
|
|
112
|
-
)
|
|
103
|
+
com.dprint(f"[SUCCESS]\t Bypassed calibration for manipulator" f" {manipulator_id}\n")
|
|
113
104
|
return ""
|
|
114
105
|
|
|
115
106
|
def _set_can_write(
|
|
@@ -120,14 +111,10 @@ class NewScaleHandler(PlatformHandler):
|
|
|
120
111
|
sio: socketio.AsyncServer,
|
|
121
112
|
) -> com.StateOutputData:
|
|
122
113
|
self.manipulators[manipulator_id].set_can_write(can_write, hours, sio)
|
|
123
|
-
com.dprint(
|
|
124
|
-
f"[SUCCESS]\t Set can_write state for manipulator" f" {manipulator_id}\n"
|
|
125
|
-
)
|
|
114
|
+
com.dprint(f"[SUCCESS]\t Set can_write state for manipulator" f" {manipulator_id}\n")
|
|
126
115
|
return com.StateOutputData(can_write, "")
|
|
127
116
|
|
|
128
|
-
def _platform_space_to_unified_space(
|
|
129
|
-
self, platform_position: list[float]
|
|
130
|
-
) -> list[float]:
|
|
117
|
+
def _platform_space_to_unified_space(self, platform_position: list[float]) -> list[float]:
|
|
131
118
|
# unified <- platform
|
|
132
119
|
# +x <- -x
|
|
133
120
|
# +y <- +z
|
|
@@ -141,9 +128,7 @@ class NewScaleHandler(PlatformHandler):
|
|
|
141
128
|
self.dimensions[2] - platform_position[3],
|
|
142
129
|
]
|
|
143
130
|
|
|
144
|
-
def _unified_space_to_platform_space(
|
|
145
|
-
self, unified_position: list[float]
|
|
146
|
-
) -> list[float]:
|
|
131
|
+
def _unified_space_to_platform_space(self, unified_position: list[float]) -> list[float]:
|
|
147
132
|
# platform <- unified
|
|
148
133
|
# +x <- -x
|
|
149
134
|
# +y <- +z
|
|
@@ -5,14 +5,11 @@ function calls, error handling, managing per-manipulator attributes, and returni
|
|
|
5
5
|
appropriate callback parameters like in :mod:`ephys_link.new_scale_handler`.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
8
10
|
import asyncio
|
|
9
11
|
import threading
|
|
10
|
-
|
|
11
|
-
# noinspection PyPackageRequirements
|
|
12
|
-
import socketio
|
|
13
|
-
|
|
14
|
-
# noinspection PyUnresolvedReferences
|
|
15
|
-
from NstMotorCtrl import NstCtrlAxis
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
16
13
|
|
|
17
14
|
import ephys_link.common as com
|
|
18
15
|
from ephys_link.platform_manipulator import (
|
|
@@ -22,6 +19,12 @@ from ephys_link.platform_manipulator import (
|
|
|
22
19
|
PlatformManipulator,
|
|
23
20
|
)
|
|
24
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
import socketio
|
|
24
|
+
|
|
25
|
+
# noinspection PyUnresolvedReferences
|
|
26
|
+
from NstMotorCtrl import NstCtrlAxis
|
|
27
|
+
|
|
25
28
|
# Constants
|
|
26
29
|
ACCELERATION_MULTIPLIER = 5
|
|
27
30
|
CUTOFF_MULTIPLIER = 0.005
|
|
@@ -85,9 +88,7 @@ class NewScaleManipulator(PlatformManipulator):
|
|
|
85
88
|
print(f"{e}\n")
|
|
86
89
|
return com.PositionalOutputData([], "Error getting position")
|
|
87
90
|
|
|
88
|
-
async def goto_pos(
|
|
89
|
-
self, position: list[float], speed: float
|
|
90
|
-
) -> com.PositionalOutputData:
|
|
91
|
+
async def goto_pos(self, position: list[float], speed: float) -> com.PositionalOutputData:
|
|
91
92
|
"""Move manipulator to position
|
|
92
93
|
|
|
93
94
|
:param position: The position to move to in mm
|
|
@@ -151,19 +152,14 @@ class NewScaleManipulator(PlatformManipulator):
|
|
|
151
152
|
if not self._can_write:
|
|
152
153
|
return com.PositionalOutputData([], "Manipulator movement canceled")
|
|
153
154
|
|
|
154
|
-
com.dprint(
|
|
155
|
-
f"[SUCCESS]\t Moved manipulator {self._id} to position"
|
|
156
|
-
f" {manipulator_final_position}\n"
|
|
157
|
-
)
|
|
155
|
+
com.dprint(f"[SUCCESS]\t Moved manipulator {self._id} to position" f" {manipulator_final_position}\n")
|
|
158
156
|
return com.PositionalOutputData(manipulator_final_position, "")
|
|
159
157
|
except Exception as e:
|
|
160
158
|
print(f"[ERROR]\t\t Moving manipulator {self._id} to position {position}")
|
|
161
159
|
print(f"{e}\n")
|
|
162
160
|
return com.PositionalOutputData([], "Error moving manipulator")
|
|
163
161
|
|
|
164
|
-
async def drive_to_depth(
|
|
165
|
-
self, depth: float, speed: int
|
|
166
|
-
) -> com.DriveToDepthOutputData:
|
|
162
|
+
async def drive_to_depth(self, depth: float, speed: int) -> com.DriveToDepthOutputData:
|
|
167
163
|
"""Drive the manipulator to a certain depth
|
|
168
164
|
|
|
169
165
|
:param depth: The depth to drive to in mm
|
|
@@ -215,10 +211,7 @@ class NewScaleManipulator(PlatformManipulator):
|
|
|
215
211
|
if not self._can_write:
|
|
216
212
|
return com.DriveToDepthOutputData(0, "Manipulator movement canceled")
|
|
217
213
|
|
|
218
|
-
com.dprint(
|
|
219
|
-
f"[SUCCESS]\t Moved manipulator {self._id} to position"
|
|
220
|
-
f" {manipulator_final_position}\n"
|
|
221
|
-
)
|
|
214
|
+
com.dprint(f"[SUCCESS]\t Moved manipulator {self._id} to position" f" {manipulator_final_position}\n")
|
|
222
215
|
return com.DriveToDepthOutputData(manipulator_final_position[3], "")
|
|
223
216
|
except Exception as e:
|
|
224
217
|
print(f"[ERROR]\t\t Moving manipulator {self._id} to depth {depth}")
|
|
@@ -231,11 +224,7 @@ class NewScaleManipulator(PlatformManipulator):
|
|
|
231
224
|
|
|
232
225
|
:return: None
|
|
233
226
|
"""
|
|
234
|
-
return (
|
|
235
|
-
self._x.CalibrateFrequency()
|
|
236
|
-
and self._y.CalibrateFrequency()
|
|
237
|
-
and self._z.CalibrateFrequency()
|
|
238
|
-
)
|
|
227
|
+
return self._x.CalibrateFrequency() and self._y.CalibrateFrequency() and self._z.CalibrateFrequency()
|
|
239
228
|
|
|
240
229
|
def get_calibrated(self) -> bool:
|
|
241
230
|
"""Return the calibration state of the manipulator.
|
|
@@ -252,9 +241,7 @@ class NewScaleManipulator(PlatformManipulator):
|
|
|
252
241
|
"""
|
|
253
242
|
self._calibrated = True
|
|
254
243
|
|
|
255
|
-
def set_can_write(
|
|
256
|
-
self, can_write: bool, hours: float, sio: socketio.AsyncServer
|
|
257
|
-
) -> None:
|
|
244
|
+
def set_can_write(self, can_write: bool, hours: float, sio: socketio.AsyncServer) -> None:
|
|
258
245
|
"""Set if the manipulator can move
|
|
259
246
|
|
|
260
247
|
:param can_write: True if the manipulator can move, False otherwise
|
|
@@ -271,9 +258,7 @@ class NewScaleManipulator(PlatformManipulator):
|
|
|
271
258
|
if can_write and hours > 0:
|
|
272
259
|
if self._reset_timer:
|
|
273
260
|
self._reset_timer.cancel()
|
|
274
|
-
self._reset_timer = threading.Timer(
|
|
275
|
-
hours * HOURS_TO_SECONDS, self.reset_can_write, [sio]
|
|
276
|
-
)
|
|
261
|
+
self._reset_timer = threading.Timer(hours * HOURS_TO_SECONDS, self.reset_can_write, [sio])
|
|
277
262
|
self._reset_timer.start()
|
|
278
263
|
|
|
279
264
|
def get_can_write(self) -> bool:
|