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.
- ephys_link/__about__.py +1 -1
- ephys_link/__main__.py +51 -105
- ephys_link/back_end/__init__.py +0 -0
- ephys_link/back_end/platform_handler.py +315 -0
- ephys_link/back_end/server.py +274 -0
- ephys_link/bindings/__init__.py +0 -0
- ephys_link/bindings/fake_binding.py +84 -0
- ephys_link/bindings/mpm_binding.py +315 -0
- ephys_link/bindings/ump_4_binding.py +157 -0
- ephys_link/front_end/__init__.py +0 -0
- ephys_link/front_end/cli.py +104 -0
- ephys_link/front_end/gui.py +204 -0
- ephys_link/utils/__init__.py +0 -0
- ephys_link/utils/base_binding.py +176 -0
- ephys_link/utils/console.py +127 -0
- ephys_link/utils/constants.py +23 -0
- ephys_link/utils/converters.py +86 -0
- ephys_link/utils/startup.py +65 -0
- ephys_link-2.0.0.dist-info/METADATA +91 -0
- ephys_link-2.0.0.dist-info/RECORD +25 -0
- {ephys_link-1.3.3.dist-info → ephys_link-2.0.0.dist-info}/WHEEL +1 -1
- {ephys_link-1.3.3.dist-info → ephys_link-2.0.0.dist-info}/licenses/LICENSE +674 -674
- ephys_link/common.py +0 -49
- ephys_link/emergency_stop.py +0 -67
- ephys_link/gui.py +0 -217
- ephys_link/platform_handler.py +0 -465
- ephys_link/platform_manipulator.py +0 -35
- ephys_link/platforms/__init__.py +0 -5
- ephys_link/platforms/new_scale_handler.py +0 -141
- ephys_link/platforms/new_scale_manipulator.py +0 -312
- ephys_link/platforms/new_scale_pathfinder_handler.py +0 -235
- ephys_link/platforms/sensapex_handler.py +0 -151
- ephys_link/platforms/sensapex_manipulator.py +0 -227
- ephys_link/platforms/ump3_handler.py +0 -57
- ephys_link/platforms/ump3_manipulator.py +0 -147
- ephys_link/resources/CP210xManufacturing.dll +0 -0
- ephys_link/resources/NstMotorCtrl.dll +0 -0
- ephys_link/resources/SiUSBXp.dll +0 -0
- ephys_link/server.py +0 -508
- ephys_link-1.3.3.dist-info/METADATA +0 -164
- ephys_link-1.3.3.dist-info/RECORD +0 -26
- {ephys_link-1.3.3.dist-info → ephys_link-2.0.0.dist-info}/entry_points.txt +0 -0
ephys_link/common.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""Commonly used functions and dictionaries
|
|
2
|
-
|
|
3
|
-
Contains globally used helper functions and typed dictionaries (to be used as
|
|
4
|
-
callback parameters)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from typing import TYPE_CHECKING
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from vbl_aquarium.models.unity import Vector4
|
|
13
|
-
|
|
14
|
-
# Debugging flag
|
|
15
|
-
DEBUG = False
|
|
16
|
-
|
|
17
|
-
# Ephys Link ASCII
|
|
18
|
-
ASCII = r"""
|
|
19
|
-
______ _ _ _ _
|
|
20
|
-
| ____| | | | | (_) | |
|
|
21
|
-
| |__ _ __ | |__ _ _ ___ | | _ _ __ | | __
|
|
22
|
-
| __| | '_ \| '_ \| | | / __| | | | | '_ \| |/ /
|
|
23
|
-
| |____| |_) | | | | |_| \__ \ | |____| | | | | <
|
|
24
|
-
|______| .__/|_| |_|\__, |___/ |______|_|_| |_|_|\_\
|
|
25
|
-
| | __/ |
|
|
26
|
-
|_| |___/
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def dprint(message: str) -> None:
|
|
31
|
-
"""Print message if debug is enabled.
|
|
32
|
-
|
|
33
|
-
:param message: Message to print.
|
|
34
|
-
:type message: str
|
|
35
|
-
:return: None
|
|
36
|
-
"""
|
|
37
|
-
if DEBUG:
|
|
38
|
-
print(message)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def vector4_to_array(vector4: Vector4) -> list[float]:
|
|
42
|
-
"""Convert a Vector4 to a list of floats.
|
|
43
|
-
|
|
44
|
-
:param vector4: Vector4 to convert.
|
|
45
|
-
:type vector4: :class:`vbl_aquarium.models.unity.Vector4`
|
|
46
|
-
:return: List of floats.
|
|
47
|
-
:rtype: list[float]
|
|
48
|
-
"""
|
|
49
|
-
return [vector4.x, vector4.y, vector4.z, vector4.w]
|
ephys_link/emergency_stop.py
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
from signal import SIGINT, SIGTERM, signal
|
|
2
|
-
from threading import Event, Thread
|
|
3
|
-
from time import sleep
|
|
4
|
-
|
|
5
|
-
from serial import Serial
|
|
6
|
-
from serial.tools import list_ports
|
|
7
|
-
|
|
8
|
-
from ephys_link.common import dprint
|
|
9
|
-
from ephys_link.server import Server
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class EmergencyStop:
|
|
13
|
-
"""Serial system for emergency stop"""
|
|
14
|
-
|
|
15
|
-
def __init__(self, server: Server, serial_port: str) -> None:
|
|
16
|
-
"""Setup serial port for emergency stop
|
|
17
|
-
|
|
18
|
-
:param server: The Ephys Link server instance
|
|
19
|
-
:type server: Server
|
|
20
|
-
:param serial_port: The serial port to poll
|
|
21
|
-
:type serial_port: str
|
|
22
|
-
:return: None
|
|
23
|
-
"""
|
|
24
|
-
self._server = server
|
|
25
|
-
self._serial_port = serial_port
|
|
26
|
-
self._poll_rate = 0.05
|
|
27
|
-
self._kill_serial_event = Event()
|
|
28
|
-
self._poll_serial_thread = Thread(target=self._poll_serial, daemon=True)
|
|
29
|
-
|
|
30
|
-
# Register signals
|
|
31
|
-
signal(SIGTERM, self._close_serial)
|
|
32
|
-
signal(SIGINT, self._close_serial)
|
|
33
|
-
|
|
34
|
-
def watch(self) -> None:
|
|
35
|
-
"""Start polling serial port for emergency stop"""
|
|
36
|
-
self._poll_serial_thread.start()
|
|
37
|
-
|
|
38
|
-
def _poll_serial(self) -> None:
|
|
39
|
-
"""Continuously poll serial port for data
|
|
40
|
-
|
|
41
|
-
Close port on kill event
|
|
42
|
-
"""
|
|
43
|
-
target_port = self._serial_port
|
|
44
|
-
if self._serial_port is None:
|
|
45
|
-
# Search for serial ports
|
|
46
|
-
for port, desc, _ in list_ports.comports():
|
|
47
|
-
if "Arduino" in desc or "USB Serial Device" in desc:
|
|
48
|
-
target_port = port
|
|
49
|
-
break
|
|
50
|
-
|
|
51
|
-
serial = Serial(target_port, 9600, timeout=self._poll_rate)
|
|
52
|
-
while not self._kill_serial_event.is_set():
|
|
53
|
-
if serial.in_waiting > 0:
|
|
54
|
-
serial.readline()
|
|
55
|
-
# Cause a break
|
|
56
|
-
dprint("[EMERGENCY STOP]\t\t Stopping all manipulators")
|
|
57
|
-
self._server.platform.stop()
|
|
58
|
-
serial.reset_input_buffer()
|
|
59
|
-
sleep(self._poll_rate)
|
|
60
|
-
print("Close poll")
|
|
61
|
-
serial.close()
|
|
62
|
-
|
|
63
|
-
def _close_serial(self, _, __) -> None:
|
|
64
|
-
"""Close the serial connection"""
|
|
65
|
-
print("[INFO]\t\t Closing serial")
|
|
66
|
-
self._kill_serial_event.set()
|
|
67
|
-
self._poll_serial_thread.join()
|
ephys_link/gui.py
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
from asyncio import run
|
|
2
|
-
from json import dumps, load
|
|
3
|
-
from os import makedirs
|
|
4
|
-
from os.path import exists
|
|
5
|
-
from socket import gethostbyname, gethostname
|
|
6
|
-
from tkinter import CENTER, RIGHT, BooleanVar, E, IntVar, StringVar, Tk, ttk
|
|
7
|
-
|
|
8
|
-
from platformdirs import user_config_dir
|
|
9
|
-
|
|
10
|
-
import ephys_link.common as com
|
|
11
|
-
from ephys_link.__about__ import __version__ as version
|
|
12
|
-
from ephys_link.emergency_stop import EmergencyStop
|
|
13
|
-
from ephys_link.server import Server
|
|
14
|
-
|
|
15
|
-
SETTINGS_DIR = f"{user_config_dir()}\\VBL\\Ephys Link"
|
|
16
|
-
SETTINGS_FILENAME = "settings.json"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class GUI:
|
|
20
|
-
"""GUI definition for Ephys Link"""
|
|
21
|
-
|
|
22
|
-
def __init__(self) -> None:
|
|
23
|
-
"""Setup and construction of the Tk GUI"""
|
|
24
|
-
|
|
25
|
-
self._root = Tk()
|
|
26
|
-
|
|
27
|
-
# Create default settings dictionary
|
|
28
|
-
settings = {
|
|
29
|
-
"ignore_updates": False,
|
|
30
|
-
"type": "sensapex",
|
|
31
|
-
"debug": False,
|
|
32
|
-
"proxy": False,
|
|
33
|
-
"proxy_address": "proxy2.virtualbrainlab.org",
|
|
34
|
-
"port": 8081,
|
|
35
|
-
"pathfinder_port": 8080,
|
|
36
|
-
"serial": "no-e-stop",
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
# Read settings.
|
|
40
|
-
if exists(f"{SETTINGS_DIR}\\{SETTINGS_FILENAME}"):
|
|
41
|
-
with open(f"{SETTINGS_DIR}\\{SETTINGS_FILENAME}") as settings_file:
|
|
42
|
-
settings = load(settings_file)
|
|
43
|
-
|
|
44
|
-
self._ignore_updates = BooleanVar(value=settings["ignore_updates"])
|
|
45
|
-
self._type = StringVar(value=settings["type"])
|
|
46
|
-
self._debug = BooleanVar(value=settings["debug"])
|
|
47
|
-
self._proxy = BooleanVar(value=settings["proxy"])
|
|
48
|
-
self._proxy_address = StringVar(value=settings["proxy_address"])
|
|
49
|
-
self._port = IntVar(value=settings["port"])
|
|
50
|
-
self._pathfinder_port = IntVar(value=settings["pathfinder_port"])
|
|
51
|
-
self._serial = StringVar(value=settings["serial"])
|
|
52
|
-
|
|
53
|
-
def launch(self) -> None:
|
|
54
|
-
"""Build and launch GUI"""
|
|
55
|
-
|
|
56
|
-
# Build and run GUI.
|
|
57
|
-
self._build_gui()
|
|
58
|
-
self._root.mainloop()
|
|
59
|
-
|
|
60
|
-
def _build_gui(self):
|
|
61
|
-
"""Build GUI"""
|
|
62
|
-
|
|
63
|
-
self._root.title(f"Ephys Link v{version}")
|
|
64
|
-
|
|
65
|
-
mainframe = ttk.Frame(self._root, padding=3)
|
|
66
|
-
mainframe.grid(column=0, row=0, sticky="news")
|
|
67
|
-
self._root.columnconfigure(0, weight=1)
|
|
68
|
-
self._root.rowconfigure(0, weight=1)
|
|
69
|
-
mainframe.columnconfigure(0, weight=1)
|
|
70
|
-
mainframe.rowconfigure(0, weight=1)
|
|
71
|
-
|
|
72
|
-
# Server serving settings.
|
|
73
|
-
|
|
74
|
-
server_serving_settings = ttk.LabelFrame(mainframe, text="Serving Settings", padding=3)
|
|
75
|
-
server_serving_settings.grid(column=0, row=0, sticky="news")
|
|
76
|
-
|
|
77
|
-
# Local IP.
|
|
78
|
-
ttk.Label(server_serving_settings, text="Local IP:", anchor=E, justify=RIGHT).grid(column=0, row=0, sticky="we")
|
|
79
|
-
ttk.Label(server_serving_settings, text=gethostbyname(gethostname())).grid(column=1, row=0, sticky="we")
|
|
80
|
-
|
|
81
|
-
# Proxy.
|
|
82
|
-
ttk.Label(server_serving_settings, text="Use Proxy:", anchor=E, justify=RIGHT).grid(
|
|
83
|
-
column=0, row=1, sticky="we"
|
|
84
|
-
)
|
|
85
|
-
ttk.Checkbutton(
|
|
86
|
-
server_serving_settings,
|
|
87
|
-
variable=self._proxy,
|
|
88
|
-
).grid(column=1, row=1, sticky="we")
|
|
89
|
-
|
|
90
|
-
# Proxy address.
|
|
91
|
-
ttk.Label(server_serving_settings, text="Proxy Address:", anchor=E, justify=RIGHT).grid(
|
|
92
|
-
column=0, row=2, sticky="we"
|
|
93
|
-
)
|
|
94
|
-
ttk.Entry(server_serving_settings, textvariable=self._proxy_address, justify=CENTER).grid(
|
|
95
|
-
column=1, row=2, sticky="we"
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
# Port.
|
|
99
|
-
ttk.Label(server_serving_settings, text="Port:", anchor=E, justify=RIGHT).grid(column=0, row=3, sticky="we")
|
|
100
|
-
ttk.Entry(server_serving_settings, textvariable=self._port, width=5, justify=CENTER).grid(
|
|
101
|
-
column=1, row=3, sticky="we"
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
# Ignore updates.
|
|
105
|
-
ttk.Label(server_serving_settings, text="Ignore Updates:", anchor=E, justify=RIGHT).grid(
|
|
106
|
-
column=0, row=4, sticky="we"
|
|
107
|
-
)
|
|
108
|
-
ttk.Checkbutton(
|
|
109
|
-
server_serving_settings,
|
|
110
|
-
variable=self._ignore_updates,
|
|
111
|
-
).grid(column=1, row=4, sticky="we")
|
|
112
|
-
|
|
113
|
-
# ---
|
|
114
|
-
|
|
115
|
-
# Platform type.
|
|
116
|
-
platform_type_settings = ttk.LabelFrame(mainframe, text="Platform Type", padding=3)
|
|
117
|
-
platform_type_settings.grid(column=0, row=1, sticky="news")
|
|
118
|
-
|
|
119
|
-
ttk.Radiobutton(
|
|
120
|
-
platform_type_settings,
|
|
121
|
-
text="Sensapex uMp-4",
|
|
122
|
-
variable=self._type,
|
|
123
|
-
value="sensapex",
|
|
124
|
-
).grid(column=0, row=0, sticky="we")
|
|
125
|
-
ttk.Radiobutton(
|
|
126
|
-
platform_type_settings,
|
|
127
|
-
text="Sensapex uMp-3",
|
|
128
|
-
variable=self._type,
|
|
129
|
-
value="ump3",
|
|
130
|
-
).grid(column=0, row=1, sticky="we")
|
|
131
|
-
ttk.Radiobutton(
|
|
132
|
-
platform_type_settings,
|
|
133
|
-
text="Pathfinder MPM Control v2.8.8+",
|
|
134
|
-
variable=self._type,
|
|
135
|
-
value="new_scale_pathfinder",
|
|
136
|
-
).grid(column=0, row=2, sticky="we")
|
|
137
|
-
ttk.Radiobutton(
|
|
138
|
-
platform_type_settings,
|
|
139
|
-
text="New Scale M3-USB-3:1-EP",
|
|
140
|
-
variable=self._type,
|
|
141
|
-
value="new_scale",
|
|
142
|
-
).grid(column=0, row=3, sticky="we")
|
|
143
|
-
|
|
144
|
-
# ---
|
|
145
|
-
|
|
146
|
-
# New Scale Settings.
|
|
147
|
-
new_scale_settings = ttk.LabelFrame(mainframe, text="Pathfinder Settings", padding=3)
|
|
148
|
-
new_scale_settings.grid(column=0, row=2, sticky="news")
|
|
149
|
-
|
|
150
|
-
# Port
|
|
151
|
-
ttk.Label(new_scale_settings, text="HTTP Server Port:", anchor=E, justify=RIGHT).grid(
|
|
152
|
-
column=0, row=1, sticky="we"
|
|
153
|
-
)
|
|
154
|
-
ttk.Entry(new_scale_settings, textvariable=self._pathfinder_port, width=5, justify=CENTER).grid(
|
|
155
|
-
column=1, row=1, sticky="we"
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# ---
|
|
159
|
-
|
|
160
|
-
# Emergency Stop serial port.
|
|
161
|
-
e_stop_settings = ttk.LabelFrame(mainframe, text="Emergency Stop Settings", padding=3)
|
|
162
|
-
e_stop_settings.grid(column=0, row=3, sticky="news")
|
|
163
|
-
|
|
164
|
-
# Serial Port
|
|
165
|
-
ttk.Label(e_stop_settings, text="Serial Port:", anchor=E, justify=RIGHT).grid(column=0, row=1, sticky="we")
|
|
166
|
-
ttk.Entry(e_stop_settings, textvariable=self._serial, justify=CENTER).grid(column=1, row=1, sticky="we")
|
|
167
|
-
|
|
168
|
-
# Server launch button.
|
|
169
|
-
ttk.Button(
|
|
170
|
-
mainframe,
|
|
171
|
-
text="Launch Server",
|
|
172
|
-
command=self._launch_server,
|
|
173
|
-
).grid(column=0, row=4, columnspan=2, sticky="we")
|
|
174
|
-
|
|
175
|
-
def _launch_server(self) -> None:
|
|
176
|
-
"""Launch server based on GUI settings"""
|
|
177
|
-
|
|
178
|
-
# Close GUI.
|
|
179
|
-
self._root.destroy()
|
|
180
|
-
|
|
181
|
-
# Save settings.
|
|
182
|
-
settings = {
|
|
183
|
-
"ignore_updates": self._ignore_updates.get(),
|
|
184
|
-
"type": self._type.get(),
|
|
185
|
-
"debug": self._debug.get(),
|
|
186
|
-
"proxy": self._proxy.get(),
|
|
187
|
-
"proxy_address": self._proxy_address.get(),
|
|
188
|
-
"port": self._port.get(),
|
|
189
|
-
"pathfinder_port": self._pathfinder_port.get(),
|
|
190
|
-
"serial": self._serial.get(),
|
|
191
|
-
}
|
|
192
|
-
makedirs(SETTINGS_DIR, exist_ok=True)
|
|
193
|
-
with open(f"{SETTINGS_DIR}\\{SETTINGS_FILENAME}", "w+") as f:
|
|
194
|
-
f.write(dumps(settings))
|
|
195
|
-
|
|
196
|
-
# Launch server.
|
|
197
|
-
server = Server()
|
|
198
|
-
|
|
199
|
-
com.DEBUG = self._debug.get()
|
|
200
|
-
|
|
201
|
-
if self._serial.get() != "no-e-stop":
|
|
202
|
-
e_stop = EmergencyStop(server, self._serial.get())
|
|
203
|
-
e_stop.watch()
|
|
204
|
-
|
|
205
|
-
# Launch with parsed arguments on main thread.
|
|
206
|
-
if self._proxy.get():
|
|
207
|
-
run(
|
|
208
|
-
server.launch_for_proxy(
|
|
209
|
-
self._proxy_address.get(),
|
|
210
|
-
self._port.get(),
|
|
211
|
-
self._type.get(),
|
|
212
|
-
self._pathfinder_port.get(),
|
|
213
|
-
self._ignore_updates.get(),
|
|
214
|
-
)
|
|
215
|
-
)
|
|
216
|
-
else:
|
|
217
|
-
server.launch(self._type.get(), self._port.get(), self._pathfinder_port.get(), self._ignore_updates.get())
|