ephys-link 2.0.0b5__tar.gz → 2.0.0b6__tar.gz

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 (36) hide show
  1. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/PKG-INFO +6 -6
  2. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/pyproject.toml +5 -5
  3. ephys_link-2.0.0b6/src/ephys_link/__about__.py +1 -0
  4. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/back_end/platform_handler.py +3 -0
  5. ephys_link-2.0.0b6/src/ephys_link/bindings/ump_3_bindings.py +138 -0
  6. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/bindings/ump_4_bindings.py +2 -1
  7. ephys_link-2.0.0b5/src/ephys_link/__about__.py +0 -1
  8. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/.gitignore +0 -0
  9. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/LICENSE +0 -0
  10. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/README.md +0 -0
  11. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/assets/icon.ico +0 -0
  12. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/ephys_link.spec +0 -0
  13. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/qodana.yaml +0 -0
  14. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/scripts/__init__.py +0 -0
  15. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/scripts/logger_test.py +0 -0
  16. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/scripts/move_tester.py +0 -0
  17. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/scripts/server_tester.py +0 -0
  18. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/__init__.py +0 -0
  19. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/__main__.py +0 -0
  20. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/back_end/__init__.py +0 -0
  21. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/back_end/server.py +0 -0
  22. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/bindings/__init__.py +0 -0
  23. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/bindings/fake_bindings.py +0 -0
  24. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/bindings/mpm_bindings.py +0 -0
  25. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/front_end/__init__.py +0 -0
  26. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/front_end/cli.py +0 -0
  27. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/front_end/gui.py +0 -0
  28. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/resources/CP210xManufacturing.dll +0 -0
  29. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/resources/NstMotorCtrl.dll +0 -0
  30. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/resources/SiUSBXp.dll +0 -0
  31. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/resources/libum.dll +0 -0
  32. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/util/__init__.py +0 -0
  33. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/util/base_bindings.py +0 -0
  34. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/util/common.py +0 -0
  35. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/src/ephys_link/util/console.py +0 -0
  36. {ephys_link-2.0.0b5 → ephys_link-2.0.0b6}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ephys-link
3
- Version: 2.0.0b5
3
+ Version: 2.0.0b6
4
4
  Summary: A Python Socket.IO server that allows any Socket.IO-compliant application to communicate with manipulators used in electrophysiology experiments.
5
5
  Project-URL: Documentation, https://virtualbrainlab.org/ephys_link/installation_and_use.html
6
6
  Project-URL: Issues, https://github.com/VirtualBrainLab/ephys-link/issues
@@ -26,14 +26,14 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
26
26
  Classifier: Programming Language :: Python :: Implementation :: PyPy
27
27
  Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
28
28
  Requires-Python: <3.13,>=3.10
29
- Requires-Dist: aiohttp==3.9.5
29
+ Requires-Dist: aiohttp==3.10.8
30
30
  Requires-Dist: colorama==0.4.6
31
- Requires-Dist: platformdirs==4.2.2
31
+ Requires-Dist: platformdirs==4.3.6
32
32
  Requires-Dist: pyserial==3.5
33
- Requires-Dist: python-socketio[asyncio-client]==5.11.3
34
- Requires-Dist: pythonnet==3.0.3
33
+ Requires-Dist: python-socketio[asyncio-client]==5.11.4
34
+ Requires-Dist: pythonnet==3.0.4
35
35
  Requires-Dist: requests==2.32.3
36
- Requires-Dist: rich==13.7.1
36
+ Requires-Dist: rich==13.8.1
37
37
  Requires-Dist: sensapex==1.400.1
38
38
  Requires-Dist: vbl-aquarium==0.0.22
39
39
  Description-Content-Type: text/markdown
@@ -30,15 +30,15 @@ classifiers = [
30
30
  "Topic :: Scientific/Engineering :: Medical Science Apps.",
31
31
  ]
32
32
  dependencies = [
33
- "aiohttp==3.9.5",
33
+ "aiohttp==3.10.8",
34
34
  "colorama==0.4.6",
35
- "platformdirs==4.2.2",
35
+ "platformdirs==4.3.6",
36
36
  "pyserial==3.5",
37
- "python-socketio[asyncio_client]==5.11.3",
38
- "pythonnet==3.0.3",
37
+ "python-socketio[asyncio_client]==5.11.4",
38
+ "pythonnet==3.0.4",
39
39
  "requests==2.32.3",
40
40
  "sensapex==1.400.1",
41
- "rich==13.7.1",
41
+ "rich==13.8.1",
42
42
  "vbl-aquarium==0.0.22"
43
43
  ]
44
44
 
@@ -0,0 +1 @@
1
+ __version__ = "2.0.0b6"
@@ -27,6 +27,7 @@ from vbl_aquarium.models.unity import Vector4
27
27
  from ephys_link.__about__ import __version__
28
28
  from ephys_link.bindings.fake_bindings import FakeBindings
29
29
  from ephys_link.bindings.mpm_bindings import MPMBinding
30
+ from ephys_link.bindings.ump_3_bindings import Ump3Bindings
30
31
  from ephys_link.bindings.ump_4_bindings import Ump4Bindings
31
32
  from ephys_link.util.base_bindings import BaseBindings
32
33
  from ephys_link.util.common import vector4_to_array
@@ -69,6 +70,8 @@ class PlatformHandler:
69
70
  match options.type:
70
71
  case "ump-4":
71
72
  return Ump4Bindings()
73
+ case "ump-3":
74
+ return Ump3Bindings()
72
75
  case "pathfinder-mpm":
73
76
  return MPMBinding(options.mpm_port)
74
77
  case "fake":
@@ -0,0 +1,138 @@
1
+ """Bindings for Sensapex uMp-3 platform.
2
+
3
+ Usage: Instantiate Ump4Bindings to interact with the Sensapex uMp-4 platform.
4
+ """
5
+
6
+ from asyncio import get_running_loop
7
+
8
+ from sensapex import UMP, SensapexDevice
9
+ from vbl_aquarium.models.unity import Vector3, Vector4
10
+
11
+ from ephys_link.util.base_bindings import BaseBindings
12
+ from ephys_link.util.common import (
13
+ RESOURCES_PATH,
14
+ array_to_vector4,
15
+ scalar_mm_to_um,
16
+ um_to_mm,
17
+ vector4_to_array,
18
+ vector_mm_to_um,
19
+ )
20
+
21
+
22
+ class Ump3Bindings(BaseBindings):
23
+ """Bindings for UMP-3 platform"""
24
+
25
+ def __init__(self) -> None:
26
+ """Initialize UMP-3 bindings."""
27
+
28
+ # Establish connection to Sensapex API (exit if connection fails).
29
+ UMP.set_library_path(RESOURCES_PATH)
30
+ self._ump = UMP.get_ump()
31
+ if self._ump is None:
32
+ error_message = "Unable to connect to uMp"
33
+ raise ValueError(error_message)
34
+
35
+ async def get_manipulators(self) -> list[str]:
36
+ return list(map(str, self._ump.list_devices()))
37
+
38
+ async def get_axes_count(self) -> int:
39
+ return 3
40
+
41
+ def get_dimensions(self) -> Vector4:
42
+ return Vector4(x=20, y=20, z=20, w=20)
43
+
44
+ async def get_position(self, manipulator_id: str) -> Vector4:
45
+ manipulator_position = self._get_device(manipulator_id).get_pos(1)
46
+
47
+ # Add the depth axis to the end of the position.
48
+ manipulator_position.append(manipulator_position[0])
49
+
50
+ # Convert and return.
51
+ return um_to_mm(array_to_vector4(manipulator_position))
52
+
53
+ # noinspection PyTypeChecker
54
+ async def get_angles(self, _: str) -> Vector3:
55
+ """uMp-3 does not support getting angles so raise an error.
56
+
57
+ :raises: AttributeError
58
+ """
59
+ error_message = "UMP-3 does not support getting angles"
60
+ raise AttributeError(error_message)
61
+
62
+ # noinspection PyTypeChecker
63
+ async def get_shank_count(self, _: str) -> int:
64
+ """uMp-3 does not support getting shank count so raise an error.
65
+
66
+ :raises: AttributeError
67
+ """
68
+ error_message = "UMP-3 does not support getting shank count"
69
+ raise AttributeError(error_message)
70
+
71
+ def get_movement_tolerance(self) -> float:
72
+ return 0.001
73
+
74
+ # noinspection DuplicatedCode
75
+ async def set_position(self, manipulator_id: str, position: Vector4, speed: float) -> Vector4:
76
+ # Convert position to micrometers.
77
+ target_position_um = vector_mm_to_um(position)
78
+
79
+ # Request movement.
80
+ movement = self._get_device(manipulator_id).goto_pos(
81
+ vector4_to_array(target_position_um), scalar_mm_to_um(speed)
82
+ )
83
+
84
+ # Wait for movement to complete.
85
+ await get_running_loop().run_in_executor(None, movement.finished_event.wait, None)
86
+
87
+ # Handle interrupted movement.
88
+ if movement.interrupted:
89
+ error_message = f"Manipulator {manipulator_id} interrupted: {movement.interrupt_reason}"
90
+ raise RuntimeError(error_message)
91
+
92
+ return um_to_mm(array_to_vector4(movement.last_pos))
93
+
94
+ async def set_depth(self, manipulator_id: str, depth: float, speed: float) -> float:
95
+ # Augment current position with depth.
96
+ current_position = await self.get_position(manipulator_id)
97
+ new_platform_position = current_position.model_copy(update={"x": depth})
98
+
99
+ # Make the movement.
100
+ final_platform_position = await self.set_position(manipulator_id, new_platform_position, speed)
101
+
102
+ # Return the final depth.
103
+ return float(final_platform_position.w)
104
+
105
+ async def stop(self, manipulator_id: str) -> None:
106
+ self._get_device(manipulator_id).stop()
107
+
108
+ def platform_space_to_unified_space(self, platform_space: Vector4) -> Vector4:
109
+ # unified <- platform
110
+ # +x <- +y
111
+ # +y <- -x
112
+ # +z <- -z
113
+ # +d <- +d/x
114
+
115
+ return Vector4(
116
+ x=platform_space.y,
117
+ y=self.get_dimensions().x - platform_space.x,
118
+ z=self.get_dimensions().z - platform_space.z,
119
+ w=platform_space.w,
120
+ )
121
+
122
+ def unified_space_to_platform_space(self, unified_space: Vector4) -> Vector4:
123
+ # platform <- unified
124
+ # +x <- -y
125
+ # +y <- +x
126
+ # +z <- -z
127
+ # +d/x <- +d
128
+
129
+ return Vector4(
130
+ x=self.get_dimensions().y - unified_space.y,
131
+ y=unified_space.x,
132
+ z=self.get_dimensions().z - unified_space.z,
133
+ w=unified_space.w,
134
+ )
135
+
136
+ # Helper methods.
137
+ def _get_device(self, manipulator_id: str) -> SensapexDevice:
138
+ return self._ump.get_device(int(manipulator_id))
@@ -65,6 +65,7 @@ class Ump4Bindings(BaseBindings):
65
65
  def get_movement_tolerance(self) -> float:
66
66
  return 0.001
67
67
 
68
+ # noinspection DuplicatedCode
68
69
  async def set_position(self, manipulator_id: str, position: Vector4, speed: float) -> Vector4:
69
70
  # Convert position to micrometers.
70
71
  target_position_um = vector_mm_to_um(position)
@@ -75,7 +76,7 @@ class Ump4Bindings(BaseBindings):
75
76
  )
76
77
 
77
78
  # Wait for movement to finish.
78
- await get_running_loop().run_in_executor(None, movement.finished_event.wait)
79
+ await get_running_loop().run_in_executor(None, movement.finished_event.wait, None)
79
80
 
80
81
  # Handle interrupted movement.
81
82
  if movement.interrupted:
@@ -1 +0,0 @@
1
- __version__ = "2.0.0b5"
File without changes
File without changes
File without changes
File without changes