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.
@@ -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.set_debug(args.debug)
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(GetManipulatorsOutputData, self).__init__(
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(PositionalOutputData, self).__init__(position=position, error=error)
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(AngularOutputData, self).__init__(angles=angles, error=error)
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(ShankCountOutputData, self).__init__(shank_count=shank_count, error=error)
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(DriveToDepthOutputData, self).__init__(depth=depth, error=error)
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(StateOutputData, self).__init__(state=state, error=error)
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 IntVar, StringVar, Tk, E, CENTER, RIGHT
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
- column=0, row=0, sticky="we"
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
- column=0, row=1, sticky="we"
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
- new_scale_settings, text="HTTP Server Port:", anchor=E, justify=RIGHT
121
- ).grid(column=0, row=1, sticky="we")
122
- ttk.Entry(
123
- new_scale_settings, textvariable=new_scale_port, width=5, justify=CENTER
124
- ).grid(column=1, row=1, sticky="we")
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
- column=0, row=1, sticky="we"
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 server_port, server_launch_button_text, is_running
139
+ global is_running
157
140
  is_running = not is_running
158
141
  if start:
159
142
  # Launch serial
@@ -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 abc import ABC, abstractmethod
15
+ from __future__ import annotations
16
16
 
17
- # noinspection PyPackageRequirements
18
- import socketio
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
- finally:
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
- raise ValueError("Manipulator ID must be numeric")
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
- raise ValueError(f"Manipulator {manipulator_id} not connected")
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
- raise ValueError(f"Manipulator {manipulator_id} has no axes")
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: