qlsdk2 0.2.0a1__py3-none-any.whl → 0.3.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.
- qlsdk/__init__.py +5 -2
- qlsdk/ar4/__init__.py +128 -0
- qlsdk/ar4m/__init__.py +16 -45
- qlsdk/persist/__init__.py +1 -0
- qlsdk/{ar4m/persist.py → persist/edf.py} +21 -13
- qlsdk/sdk/__init__.py +2 -0
- qlsdk/sdk/ar4sdk.py +742 -0
- qlsdk/sdk/hub.py +51 -0
- qlsdk/x8/__init__.py +128 -0
- qlsdk/x8m/__init__.py +21 -0
- {qlsdk2-0.2.0a1.dist-info → qlsdk2-0.3.0.dist-info}/METADATA +1 -1
- qlsdk2-0.3.0.dist-info/RECORD +16 -0
- qlsdk/ar4m/ar4sdk.py +0 -1011
- qlsdk2-0.2.0a1.dist-info/RECORD +0 -10
- /qlsdk/{ar4m → sdk}/libs/libAr4SDK.dll +0 -0
- /qlsdk/{ar4m → sdk}/libs/libwinpthread-1.dll +0 -0
- {qlsdk2-0.2.0a1.dist-info → qlsdk2-0.3.0.dist-info}/WHEEL +0 -0
- {qlsdk2-0.2.0a1.dist-info → qlsdk2-0.3.0.dist-info}/top_level.txt +0 -0
qlsdk/sdk/ar4sdk.py
ADDED
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from multiprocessing import Queue
|
|
3
|
+
import platform
|
|
4
|
+
from ctypes import (
|
|
5
|
+
c_int, c_int32, c_int64, c_uint32, c_uint64,
|
|
6
|
+
c_char, c_char_p, c_void_p, c_double,
|
|
7
|
+
Structure, POINTER, CFUNCTYPE
|
|
8
|
+
)
|
|
9
|
+
from typing import Literal
|
|
10
|
+
from loguru import logger
|
|
11
|
+
from time import sleep, time
|
|
12
|
+
import os
|
|
13
|
+
import numpy as np
|
|
14
|
+
# from .persist import EdfHandler
|
|
15
|
+
|
|
16
|
+
real_path = os.path.realpath(__file__)
|
|
17
|
+
dll_path = f'{os.path.dirname(real_path)}/libs/libAr4SDK.dll'
|
|
18
|
+
|
|
19
|
+
# 加载 DLL
|
|
20
|
+
if platform.system() == 'Windows':
|
|
21
|
+
_dll = ctypes.CDLL(dll_path)
|
|
22
|
+
else:
|
|
23
|
+
raise NotImplementedError(f"不支持非Windows平台:{platform.system()}")
|
|
24
|
+
|
|
25
|
+
#------------------------------------------------------------------
|
|
26
|
+
# 基础类型定义
|
|
27
|
+
#------------------------------------------------------------------
|
|
28
|
+
class Ar4MacStr(Structure):
|
|
29
|
+
_fields_ = [("str", c_char * 17)]
|
|
30
|
+
|
|
31
|
+
class Ar4Name(Structure):
|
|
32
|
+
_fields_ = [("str", c_char * 21)]
|
|
33
|
+
|
|
34
|
+
class Ar4Device(Structure):
|
|
35
|
+
_fields_ = [
|
|
36
|
+
("slot", c_int32),
|
|
37
|
+
("mac", c_uint64),
|
|
38
|
+
("hub_name", Ar4Name)
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
class Ar4NotifyData(Structure):
|
|
42
|
+
_fields_ = [
|
|
43
|
+
("time_stamp", c_int64),
|
|
44
|
+
("pkg_id", c_int64),
|
|
45
|
+
("notify_id", c_int64),
|
|
46
|
+
("eeg", POINTER(c_int32)),
|
|
47
|
+
("eeg_ch_count", c_int32),
|
|
48
|
+
("eeg_count", c_int32),
|
|
49
|
+
("acc", POINTER(c_int32)),
|
|
50
|
+
("acc_ch_count", c_int32),
|
|
51
|
+
("acc_count", c_int32)
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
#------------------------------------------------------------------
|
|
55
|
+
# 回调函数类型
|
|
56
|
+
#------------------------------------------------------------------
|
|
57
|
+
FuncAr4DataNotify = CFUNCTYPE(None, c_void_p, POINTER(Ar4NotifyData))
|
|
58
|
+
FuncAr4TriggerNotify = CFUNCTYPE(None, c_void_p, c_int64, c_int32, c_uint32)
|
|
59
|
+
FuncAr4RecorderDisconnected = CFUNCTYPE(None, c_void_p)
|
|
60
|
+
FuncAr4RecorderConnected = CFUNCTYPE(None, c_void_p)
|
|
61
|
+
FuncAr4RecorderTimeout = CFUNCTYPE(None, c_void_p, c_int64)
|
|
62
|
+
FuncAr4RecorderPkgLost = CFUNCTYPE(c_int, c_void_p, c_int64, c_int64, c_int64)
|
|
63
|
+
FuncAr4GetTime = CFUNCTYPE(c_int64)
|
|
64
|
+
FuncAr4SDKPrint = CFUNCTYPE(None, c_char_p, ctypes.c_size_t)
|
|
65
|
+
|
|
66
|
+
#------------------------------------------------------------------
|
|
67
|
+
# SDK 函数定义
|
|
68
|
+
#------------------------------------------------------------------
|
|
69
|
+
# 设备枚举
|
|
70
|
+
_dll.ar4_sdk_enum_device.restype = POINTER(Ar4Device)
|
|
71
|
+
_dll.ar4_sdk_enum_hub.restype = POINTER(Ar4Name)
|
|
72
|
+
_dll.ar4_sdk_enum_in_hub.argtypes = [Ar4Name]
|
|
73
|
+
|
|
74
|
+
# MAC地址转换
|
|
75
|
+
_dll.ar4_mac_to_str.argtypes = [c_uint64]
|
|
76
|
+
_dll.ar4_mac_to_str.restype = Ar4MacStr
|
|
77
|
+
_dll.ar4_str_to_mac.argtypes = [Ar4MacStr]
|
|
78
|
+
_dll.ar4_str_to_mac.restype = c_uint64
|
|
79
|
+
|
|
80
|
+
# 连接管理
|
|
81
|
+
_dll.ar4_sdk_connect.argtypes = [c_uint64, c_void_p]
|
|
82
|
+
_dll.ar4_sdk_connect.restype = c_void_p
|
|
83
|
+
_dll.ar4_sdk_disconnect.argtypes = [c_void_p]
|
|
84
|
+
|
|
85
|
+
# AR4信息查询
|
|
86
|
+
_dll.ar4_sdk_get_box_name.argtypes = [c_void_p]
|
|
87
|
+
_dll.ar4_sdk_get_box_name.restype = c_uint64
|
|
88
|
+
|
|
89
|
+
# 数据采集控制
|
|
90
|
+
_dll.ar4_sdk_start_acq.argtypes = [c_void_p]
|
|
91
|
+
_dll.ar4_sdk_start_acq.restype = c_int
|
|
92
|
+
_dll.ar4_sdk_stop_acq.argtypes = [c_void_p]
|
|
93
|
+
_dll.ar4_sdk_stop_acq.restype = c_int
|
|
94
|
+
_dll.ar4_sdk_get_record_sample_rate.argtypes = [c_void_p]
|
|
95
|
+
_dll.ar4_sdk_get_record_sample_rate.restype = c_double
|
|
96
|
+
_dll.ar4_sdk_get_acq_start_time.argtypes = [c_void_p]
|
|
97
|
+
_dll.ar4_sdk_get_acq_start_time.restype = c_int64
|
|
98
|
+
|
|
99
|
+
# eeg信号物理量转换
|
|
100
|
+
_dll.ar4_sdk_get_eeg_phy_max.argtypes = [c_void_p]
|
|
101
|
+
_dll.ar4_sdk_get_eeg_phy_max.restype = c_double
|
|
102
|
+
_dll.ar4_sdk_get_eeg_phy_min.argtypes = [c_void_p]
|
|
103
|
+
_dll.ar4_sdk_get_eeg_phy_min.restype = c_double
|
|
104
|
+
_dll.ar4_sdk_get_eeg_digtal_max.argtypes = [c_void_p]
|
|
105
|
+
_dll.ar4_sdk_get_eeg_digtal_max.restype = c_int32
|
|
106
|
+
_dll.ar4_sdk_get_eeg_digtal_min.argtypes = [c_void_p]
|
|
107
|
+
_dll.ar4_sdk_get_eeg_digtal_min.restype = c_int32
|
|
108
|
+
_dll.ar4_sdk_get_eeg_phy_unit.argtypes = [c_void_p]
|
|
109
|
+
_dll.ar4_sdk_get_eeg_phy_unit.restype = c_char_p
|
|
110
|
+
|
|
111
|
+
# acc信号物理量转换
|
|
112
|
+
_dll.ar4_sdk_get_acc_phy_max.argtypes = [c_void_p]
|
|
113
|
+
_dll.ar4_sdk_get_acc_phy_max.restype = c_double
|
|
114
|
+
_dll.ar4_sdk_get_acc_phy_min.argtypes = [c_void_p]
|
|
115
|
+
_dll.ar4_sdk_get_acc_phy_min.restype = c_double
|
|
116
|
+
_dll.ar4_sdk_get_acc_digtal_max.argtypes = [c_void_p]
|
|
117
|
+
_dll.ar4_sdk_get_acc_digtal_max.restype = c_int32
|
|
118
|
+
_dll.ar4_sdk_get_acc_digtal_min.argtypes = [c_void_p]
|
|
119
|
+
_dll.ar4_sdk_get_acc_digtal_min.restype = c_int32
|
|
120
|
+
_dll.ar4_sdk_get_acc_phy_unit.argtypes = [c_void_p]
|
|
121
|
+
_dll.ar4_sdk_get_acc_phy_unit.restype = c_char_p
|
|
122
|
+
|
|
123
|
+
# 回调注册
|
|
124
|
+
_dll.ar4_sdk_register_data_notify.argtypes = [c_void_p, FuncAr4DataNotify]
|
|
125
|
+
_dll.ar4_sdk_register_data_notify.restype = None
|
|
126
|
+
_dll.ar4_sdk_register_trigger_notify.argtypes = [c_void_p, c_void_p]
|
|
127
|
+
_dll.ar4_sdk_register_conn_notify.argtypes = [c_void_p, FuncAr4RecorderConnected, FuncAr4RecorderDisconnected]
|
|
128
|
+
_dll.ar4_sdk_register_record_state_notify.argtypes = [c_void_p, FuncAr4RecorderConnected, FuncAr4RecorderDisconnected]
|
|
129
|
+
_dll.ar4_sdk_register_box_conn_notify.argtypes = [c_void_p, FuncAr4RecorderConnected, FuncAr4RecorderDisconnected]
|
|
130
|
+
|
|
131
|
+
# _dll对外封装类
|
|
132
|
+
class AR4SDK:
|
|
133
|
+
@classmethod
|
|
134
|
+
def enum_devices(cls):
|
|
135
|
+
"""枚举可用设备"""
|
|
136
|
+
devices = []
|
|
137
|
+
ptr = _dll.ar4_sdk_enum_device()
|
|
138
|
+
if not ptr:
|
|
139
|
+
logger.info("没有找到ar4设备")
|
|
140
|
+
return devices
|
|
141
|
+
|
|
142
|
+
index = 0
|
|
143
|
+
while True:
|
|
144
|
+
device = ptr[index]
|
|
145
|
+
if not device or device.mac == 0:
|
|
146
|
+
break
|
|
147
|
+
# logger.debug(f"get device: {device.slot}, mac: {hex(device.mac)}, hub_name: {device.hub_name.str}")
|
|
148
|
+
devices.append(device)
|
|
149
|
+
index += 1
|
|
150
|
+
|
|
151
|
+
return devices
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def enum_hubs(cls):
|
|
155
|
+
"""枚举可用设备"""
|
|
156
|
+
devices = []
|
|
157
|
+
ptr = _dll.ar4_sdk_enum_hub()
|
|
158
|
+
sleep(5)
|
|
159
|
+
logger.debug(f"enum_hubs: {ptr}")
|
|
160
|
+
|
|
161
|
+
def __enter__(self):
|
|
162
|
+
return self
|
|
163
|
+
|
|
164
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# 枚举后才能连接上
|
|
169
|
+
AR4SDK.enum_devices()
|
|
170
|
+
# 读取系统当前时间(ms)
|
|
171
|
+
def _get_time():
|
|
172
|
+
cur_time = int(round(time()) * 1000)
|
|
173
|
+
return cur_time
|
|
174
|
+
|
|
175
|
+
class Packet(object):
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
class LMPacket(Packet):
|
|
179
|
+
def __init__(self):
|
|
180
|
+
self.time_stamp = None
|
|
181
|
+
self.pkg_id = None
|
|
182
|
+
self.notify_id = None
|
|
183
|
+
self.eeg_ch_count = None
|
|
184
|
+
self.eeg_count = None
|
|
185
|
+
self.eeg = None
|
|
186
|
+
self.acc_ch_count = None
|
|
187
|
+
self.acc_count = None
|
|
188
|
+
self.acc = None
|
|
189
|
+
|
|
190
|
+
def transfer(self, data: Ar4NotifyData):
|
|
191
|
+
self.time_stamp = data.time_stamp
|
|
192
|
+
self.pkg_id = data.pkg_id
|
|
193
|
+
self.notify_id = data.notify_id
|
|
194
|
+
self.eeg_ch_count = data.eeg_ch_count
|
|
195
|
+
self.eeg_count = data.eeg_count
|
|
196
|
+
self.acc_ch_count = data.acc_ch_count
|
|
197
|
+
self.acc_count = data.acc_count
|
|
198
|
+
# 读eeg数据
|
|
199
|
+
if self.eeg_ch_count and self.eeg_count:
|
|
200
|
+
self.eeg = [data.eeg[i:self.eeg_count*self.eeg_ch_count:self.eeg_ch_count] for i in range(self.eeg_ch_count)]
|
|
201
|
+
# 读acc数据
|
|
202
|
+
if self.acc_ch_count and self.acc_count:
|
|
203
|
+
self.acc = [[] for _ in range(self.acc_ch_count)]
|
|
204
|
+
for i in range(self.acc_ch_count):
|
|
205
|
+
self.acc[i] = [data.acc[j + (i * self.acc_count)] for j in range(self.acc_count)]
|
|
206
|
+
|
|
207
|
+
return self
|
|
208
|
+
|
|
209
|
+
def __str__(self):
|
|
210
|
+
return f"""
|
|
211
|
+
time_stamp: {self.time_stamp}
|
|
212
|
+
pkg_id: {self.pkg_id}
|
|
213
|
+
notify_id: {self.notify_id}
|
|
214
|
+
eeg_ch_count: {self.eeg_ch_count}
|
|
215
|
+
eeg_count: {self.eeg_count}
|
|
216
|
+
acc_ch_count: {self.acc_ch_count}
|
|
217
|
+
acc_count: {self.acc_count}
|
|
218
|
+
eeg: {self.eeg}
|
|
219
|
+
acc: {self.acc}
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
class LMDevice(object):
|
|
223
|
+
def __init__(self, box_mac:str):
|
|
224
|
+
# 设备句柄
|
|
225
|
+
self._handle = None
|
|
226
|
+
# 设备基本信息
|
|
227
|
+
self._box_type = None
|
|
228
|
+
self._box_mac = box_mac
|
|
229
|
+
self._box_id = None
|
|
230
|
+
self._box_soc = None
|
|
231
|
+
self._box_name = None
|
|
232
|
+
self._box_version = None
|
|
233
|
+
self._head_type = None
|
|
234
|
+
self._head_mac = None
|
|
235
|
+
self._head_version = None
|
|
236
|
+
self._head_conn_state = None
|
|
237
|
+
self._head_soc = None
|
|
238
|
+
self._net_state = None
|
|
239
|
+
# self._hub_name = hub_name
|
|
240
|
+
# self._slot = slot
|
|
241
|
+
self._connected = False
|
|
242
|
+
self._conn_time = None
|
|
243
|
+
self._last_time = None
|
|
244
|
+
|
|
245
|
+
self._recording = False
|
|
246
|
+
self._record_start_time = None
|
|
247
|
+
|
|
248
|
+
## eeg 数据
|
|
249
|
+
self._eeg_phy_max = None
|
|
250
|
+
self._eeg_phy_min = None
|
|
251
|
+
self._eeg_dig_max = None
|
|
252
|
+
self._eeg_dig_min = None
|
|
253
|
+
self._eeg_phy_unit = 'uV'
|
|
254
|
+
self._eeg_phy_range = None
|
|
255
|
+
self._eeg_dig_range = None
|
|
256
|
+
## acc 数据
|
|
257
|
+
self._acc_phy_max = None
|
|
258
|
+
self._acc_phy_min = None
|
|
259
|
+
self._acc_dig_max = None
|
|
260
|
+
self._acc_dig_min = None
|
|
261
|
+
self._acc_phy_unit = 'mG'
|
|
262
|
+
self._acc_phy_range = None
|
|
263
|
+
self._acc_dig_range = None
|
|
264
|
+
|
|
265
|
+
self._sample_frequency = 500
|
|
266
|
+
|
|
267
|
+
self._acq_info = {}
|
|
268
|
+
|
|
269
|
+
# 回调函数
|
|
270
|
+
self._data_callback = FuncAr4DataNotify(self._wrap_data_accept())
|
|
271
|
+
self._trigger_callback = FuncAr4TriggerNotify(self._wrap_trigger_accept())
|
|
272
|
+
self._connected_notify_callback = FuncAr4RecorderConnected(self._wrap_connected_notify())
|
|
273
|
+
self._disconnected_notify_callback = FuncAr4RecorderDisconnected(self._wrap_disconnected_notify())
|
|
274
|
+
self._box_connected_notify_callback = FuncAr4RecorderConnected(self._wrap_box_connected_notify())
|
|
275
|
+
self._box_disconnected_notify_callback = FuncAr4RecorderDisconnected(self._wrap_box_disconnected_notify())
|
|
276
|
+
self._start_record_notify_callback = FuncAr4RecorderConnected(self._wrap_start_record_notify())
|
|
277
|
+
self._stop_record_notify_callback = FuncAr4RecorderDisconnected(self._wrap_stop_record_notify())
|
|
278
|
+
|
|
279
|
+
self.init()
|
|
280
|
+
|
|
281
|
+
@property
|
|
282
|
+
def connected(self):
|
|
283
|
+
return self._connected
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def sample_frequency(self):
|
|
287
|
+
return self._sample_frequency
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def box_mac(self):
|
|
291
|
+
return self._box_mac
|
|
292
|
+
@property
|
|
293
|
+
def slot(self):
|
|
294
|
+
return self._slot
|
|
295
|
+
@property
|
|
296
|
+
def hub_name(self):
|
|
297
|
+
return self._hub_name
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def eeg_phy_max(self):
|
|
301
|
+
return self._eeg_phy_max
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def eeg_phy_min(self):
|
|
305
|
+
return self._eeg_phy_min
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def eeg_dig_max(self):
|
|
309
|
+
return self._eeg_dig_max
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def eeg_dig_min(self):
|
|
313
|
+
return self._eeg_dig_min
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def acc_phy_max(self):
|
|
317
|
+
return self._acc_phy_max
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def acc_phy_min(self):
|
|
321
|
+
return self._acc_phy_min
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def acc_dig_max(self):
|
|
325
|
+
return self._acc_dig_max
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def acc_dig_min(self):
|
|
329
|
+
return self._acc_dig_min
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def eeg_phy_unit(self):
|
|
333
|
+
return self._eeg_phy_unit
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def acc_phy_unit(self):
|
|
337
|
+
return self._acc_phy_unit
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
def eeg_phy_range(self):
|
|
341
|
+
return self._eeg_phy_range
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def eeg_dig_range(self):
|
|
345
|
+
return self._eeg_dig_range
|
|
346
|
+
|
|
347
|
+
@property
|
|
348
|
+
def acc_phy_range(self):
|
|
349
|
+
return self._acc_phy_range
|
|
350
|
+
|
|
351
|
+
@property
|
|
352
|
+
def acc_dig_range(self):
|
|
353
|
+
return self._acc_dig_range
|
|
354
|
+
|
|
355
|
+
def init(self):
|
|
356
|
+
if self.connect():
|
|
357
|
+
self.get_sample_rate()
|
|
358
|
+
## eeg 参数
|
|
359
|
+
self.get_eeg_phy_max()
|
|
360
|
+
self.get_eeg_phy_min()
|
|
361
|
+
self.get_eeg_phy_unit()
|
|
362
|
+
self.get_eeg_digital_max()
|
|
363
|
+
self.get_eeg_digital_min()
|
|
364
|
+
self._eeg_phy_range = self._eeg_phy_max - self._eeg_phy_min
|
|
365
|
+
self._eeg_dig_range = self._eeg_dig_max - self._eeg_dig_min
|
|
366
|
+
|
|
367
|
+
## acc 参数
|
|
368
|
+
self.get_acc_phy_max()
|
|
369
|
+
self.get_acc_phy_min()
|
|
370
|
+
self.get_acc_phy_unit()
|
|
371
|
+
self.get_acc_digital_max()
|
|
372
|
+
self.get_acc_digital_min()
|
|
373
|
+
self._acc_phy_range = self._acc_phy_max - self._acc_phy_min
|
|
374
|
+
self._acc_dig_range = self._acc_dig_max - self._acc_dig_min
|
|
375
|
+
|
|
376
|
+
logger.debug(self)
|
|
377
|
+
self._register_callback()
|
|
378
|
+
return True
|
|
379
|
+
else:
|
|
380
|
+
logger.error(f"设备[{self._box_mac}]连接失败")
|
|
381
|
+
return False
|
|
382
|
+
|
|
383
|
+
def get_eeg_phy_max(self):
|
|
384
|
+
if self._handle:
|
|
385
|
+
try:
|
|
386
|
+
self._eeg_phy_max = _dll.ar4_sdk_get_eeg_phy_max(self._handle)
|
|
387
|
+
except Exception as e:
|
|
388
|
+
logger.error(f"设备[{self._box_mac}]获取eeg物理最大值异常: {str(e)}")
|
|
389
|
+
|
|
390
|
+
return self._eeg_phy_max
|
|
391
|
+
|
|
392
|
+
def get_eeg_phy_min(self):
|
|
393
|
+
if self._handle:
|
|
394
|
+
try:
|
|
395
|
+
self._eeg_phy_min = _dll.ar4_sdk_get_eeg_phy_min(self._handle)
|
|
396
|
+
except Exception as e:
|
|
397
|
+
logger.error(f"设备[{self._box_mac}]获取eeg物理最小值异常: {str(e)}")
|
|
398
|
+
|
|
399
|
+
return self._eeg_phy_min
|
|
400
|
+
|
|
401
|
+
def get_eeg_phy_unit(self):
|
|
402
|
+
if self._handle:
|
|
403
|
+
try:
|
|
404
|
+
eeg_unit = _dll.ar4_sdk_get_eeg_phy_unit(self._handle)
|
|
405
|
+
if eeg_unit:
|
|
406
|
+
self._eeg_phy_unit = eeg_unit.decode("utf-8")
|
|
407
|
+
except Exception as e:
|
|
408
|
+
logger.error(f"设备[{self._box_mac}]获取eeg物理单位异常: {str(e)}")
|
|
409
|
+
|
|
410
|
+
return self._eeg_phy_unit
|
|
411
|
+
|
|
412
|
+
def get_eeg_digital_max(self):
|
|
413
|
+
if self._handle:
|
|
414
|
+
try:
|
|
415
|
+
self._eeg_dig_max = _dll.ar4_sdk_get_eeg_digtal_max(self._handle)
|
|
416
|
+
except Exception as e:
|
|
417
|
+
logger.error(f"设备[{self._box_mac}]获取eeg数字最大值异常: {str(e)}")
|
|
418
|
+
|
|
419
|
+
return self._eeg_dig_max
|
|
420
|
+
|
|
421
|
+
def get_eeg_digital_min(self):
|
|
422
|
+
if self._handle:
|
|
423
|
+
try:
|
|
424
|
+
self._eeg_dig_min = _dll.ar4_sdk_get_eeg_digtal_min(self._handle)
|
|
425
|
+
except Exception as e:
|
|
426
|
+
logger.error(f"设备[{self._box_mac}]获取eeg数字最小值异常: {str(e)}")
|
|
427
|
+
|
|
428
|
+
return self._eeg_dig_min
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def get_acc_phy_max(self):
|
|
432
|
+
if self._handle:
|
|
433
|
+
try:
|
|
434
|
+
self._acc_phy_max = _dll.ar4_sdk_get_acc_phy_max(self._handle)
|
|
435
|
+
except Exception as e:
|
|
436
|
+
logger.error(f"设备[{self._box_mac}]获取acc物理最大值异常: {str(e)}")
|
|
437
|
+
|
|
438
|
+
return self._acc_phy_max
|
|
439
|
+
|
|
440
|
+
def get_acc_phy_min(self):
|
|
441
|
+
if self._handle:
|
|
442
|
+
try:
|
|
443
|
+
self._acc_phy_min = _dll.ar4_sdk_get_acc_phy_min(self._handle)
|
|
444
|
+
except Exception as e:
|
|
445
|
+
logger.error(f"设备[{self._box_mac}]获取acc物理最小值异常: {str(e)}")
|
|
446
|
+
|
|
447
|
+
return self._eeg_phy_min
|
|
448
|
+
|
|
449
|
+
def get_acc_phy_unit(self):
|
|
450
|
+
if self._handle:
|
|
451
|
+
try:
|
|
452
|
+
acc_unit = _dll.ar4_sdk_get_acc_phy_unit(self._handle)
|
|
453
|
+
if acc_unit:
|
|
454
|
+
self._acc_phy_unit = acc_unit.decode("utf-8")
|
|
455
|
+
except Exception as e:
|
|
456
|
+
logger.error(f"设备[{self._box_mac}]获取acc物理单位异常: {str(e)}")
|
|
457
|
+
|
|
458
|
+
return self._acc_phy_unit
|
|
459
|
+
|
|
460
|
+
def get_acc_digital_max(self):
|
|
461
|
+
if self._handle:
|
|
462
|
+
try:
|
|
463
|
+
self._acc_dig_max = _dll.ar4_sdk_get_acc_digtal_max(self._handle)
|
|
464
|
+
except Exception as e:
|
|
465
|
+
logger.error(f"设备[{self._box_mac}]获取acc数字最大值异常: {str(e)}")
|
|
466
|
+
|
|
467
|
+
return self._acc_dig_max
|
|
468
|
+
|
|
469
|
+
def get_acc_digital_min(self):
|
|
470
|
+
if self._handle:
|
|
471
|
+
try:
|
|
472
|
+
self._acc_dig_min = _dll.ar4_sdk_get_acc_digtal_min(self._handle)
|
|
473
|
+
except Exception as e:
|
|
474
|
+
logger.error(f"设备[{self._box_mac}]获取acc数字最小值异常: {str(e)}")
|
|
475
|
+
|
|
476
|
+
return self._acc_dig_min
|
|
477
|
+
|
|
478
|
+
def _register_callback(self):
|
|
479
|
+
try:
|
|
480
|
+
_dll.ar4_sdk_register_data_notify(self._handle, self._data_callback)
|
|
481
|
+
_dll.ar4_sdk_register_trigger_notify(self._handle, self._trigger_callback)
|
|
482
|
+
_dll.ar4_sdk_register_conn_notify(self._handle, self._connected_notify_callback, self._disconnected_notify_callback)
|
|
483
|
+
_dll.ar4_sdk_register_record_state_notify(self._handle, self._start_record_notify_callback, self._stop_record_notify_callback)
|
|
484
|
+
_dll.ar4_sdk_register_box_conn_notify(self._handle, self._box_connected_notify_callback, self._box_disconnected_notify_callback)
|
|
485
|
+
except Exception as e:
|
|
486
|
+
logger.error(f"设备[{self._box_mac}]回调函数注册异常: {str(e)}")
|
|
487
|
+
|
|
488
|
+
def update_info(self):
|
|
489
|
+
self._last_time = _get_time()
|
|
490
|
+
|
|
491
|
+
# 设备连接
|
|
492
|
+
def connect(self)-> bool:
|
|
493
|
+
|
|
494
|
+
if not self._box_mac:
|
|
495
|
+
raise Exception("设备MAC地址不能为空")
|
|
496
|
+
|
|
497
|
+
try:
|
|
498
|
+
self._handle = _dll.ar4_sdk_connect(int(self._box_mac, 16), c_void_p(0))
|
|
499
|
+
# logger.info(f"conn handle is {self._handle}")
|
|
500
|
+
self._connected = self._handle is not None
|
|
501
|
+
if self._handle:
|
|
502
|
+
self._connected = True
|
|
503
|
+
self._handle = c_void_p(self._handle)
|
|
504
|
+
self._conn_time = _get_time()
|
|
505
|
+
self.get_box_name()
|
|
506
|
+
self.get_head_mac()
|
|
507
|
+
self.get_record_conn_state()
|
|
508
|
+
self._last_time = _get_time()
|
|
509
|
+
else:
|
|
510
|
+
raise Exception(f"设备 {self._box_mac} 连接失败: {self._handle}")
|
|
511
|
+
logger.debug(f"ar4 {self._box_mac} 连接: {self._handle}")
|
|
512
|
+
except Exception as e:
|
|
513
|
+
logger.error(f"ar4 {self._box_mac} 连接异常: {str(e)}")
|
|
514
|
+
|
|
515
|
+
return self._connected
|
|
516
|
+
|
|
517
|
+
# 读取盒子名称
|
|
518
|
+
def get_box_name(self):
|
|
519
|
+
if self._handle:
|
|
520
|
+
try:
|
|
521
|
+
self._head_mac = _dll.ar4_sdk_get_recoder_mac(self._handle)
|
|
522
|
+
except Exception as e:
|
|
523
|
+
logger.error(f"ar4 {self._box_mac} 获取盒子记录子mac异常: {str(e)}")
|
|
524
|
+
# 读取记录子mac
|
|
525
|
+
def get_head_mac(self):
|
|
526
|
+
if self._handle:
|
|
527
|
+
try:
|
|
528
|
+
self._box_name = _dll.ar4_sdk_get_record_conn_state(self._handle)
|
|
529
|
+
except Exception as e:
|
|
530
|
+
logger.error(f"ar4 {self._box_mac} 获取盒子名称异常: {str(e)}")
|
|
531
|
+
# 读取记录子连接状态
|
|
532
|
+
def get_record_conn_state(self):
|
|
533
|
+
if self._handle:
|
|
534
|
+
try:
|
|
535
|
+
self._head_conn_state = _dll.ar4_sdk_get_record_conn_state(self._handle)
|
|
536
|
+
except Exception as e:
|
|
537
|
+
logger.error(f"ar4 {self._box_mac} 获取盒子记录子连接状态异常: {str(e)}")
|
|
538
|
+
|
|
539
|
+
# 数据采集启动
|
|
540
|
+
def start_acquisition(self):
|
|
541
|
+
logger.info(f"device {self._box_mac} 启动数据采集...")
|
|
542
|
+
if not self._handle:
|
|
543
|
+
self.init()
|
|
544
|
+
|
|
545
|
+
self._acq_info["start_time"] = _get_time()
|
|
546
|
+
|
|
547
|
+
if self._handle:
|
|
548
|
+
# 启动采集
|
|
549
|
+
try:
|
|
550
|
+
logger.debug(f"device {self._box_mac} 启动采集: {self._handle}")
|
|
551
|
+
ret = _dll.ar4_sdk_start_acq(self._handle)
|
|
552
|
+
if ret == 0:
|
|
553
|
+
logger.info(f"device {self._box_mac} 启动数据采集成功")
|
|
554
|
+
self.start_record()
|
|
555
|
+
else:
|
|
556
|
+
logger.error(f"device {self._box_mac} 启动数据采集失败, ret: {ret}")
|
|
557
|
+
return ret == 0
|
|
558
|
+
except Exception as e:
|
|
559
|
+
logger.error(f"device {self._box_mac} 停止采集异常: {str(e)}")
|
|
560
|
+
else:
|
|
561
|
+
logger.info(f"device {self._box_mac} 启动数据采集失败, 设备未连接")
|
|
562
|
+
return False
|
|
563
|
+
|
|
564
|
+
def stop_acquisition(self):
|
|
565
|
+
"""停止采集"""
|
|
566
|
+
if self._handle:
|
|
567
|
+
try:
|
|
568
|
+
self.get_acq_start_time()
|
|
569
|
+
except Exception as e:
|
|
570
|
+
logger.error(f"ar4 {self._box_mac} 获取开始采集时间异常: {str(e)}")
|
|
571
|
+
try:
|
|
572
|
+
ret = _dll.ar4_sdk_stop_acq(self._handle)
|
|
573
|
+
if ret == 0:
|
|
574
|
+
logger.info(f"device {self._box_mac} 停止采集成功")
|
|
575
|
+
self.stop_record()
|
|
576
|
+
else:
|
|
577
|
+
logger.error(f"device {self._box_mac} 停止采集失败, ret: {ret}")
|
|
578
|
+
|
|
579
|
+
return ret == 0
|
|
580
|
+
except Exception as e:
|
|
581
|
+
logger.error(f"ar4 {self._box_mac} 停止采集异常: {str(e)}")
|
|
582
|
+
else:
|
|
583
|
+
return False
|
|
584
|
+
|
|
585
|
+
def start_record(self):
|
|
586
|
+
pass
|
|
587
|
+
def stop_record(self):
|
|
588
|
+
pass
|
|
589
|
+
|
|
590
|
+
def disconnect(self):
|
|
591
|
+
"""断开连接"""
|
|
592
|
+
if self._handle:
|
|
593
|
+
_dll.ar4_sdk_disconnect(self._handle)
|
|
594
|
+
|
|
595
|
+
def get_sample_rate(self):
|
|
596
|
+
try:
|
|
597
|
+
ret = _dll.ar4_sdk_get_record_sample_rate(self._handle)
|
|
598
|
+
logger.debug(f"ar4 {self._box_mac} 获取采样率: {ret}")
|
|
599
|
+
if ret > 1:
|
|
600
|
+
self._sample_frequency = ret
|
|
601
|
+
except Exception as e:
|
|
602
|
+
logger.error(f"ar4 {self._box_mac} 获取采样率异常: {str(e)}")
|
|
603
|
+
|
|
604
|
+
def get_acq_start_time(self):
|
|
605
|
+
try:
|
|
606
|
+
ret = _dll.ar4_sdk_get_acq_start_time(self._handle)
|
|
607
|
+
# 更新采样开始时间
|
|
608
|
+
if ret:
|
|
609
|
+
self._acq_info["start_time"] = ret
|
|
610
|
+
logger.debug(f"ar4 {self._box_mac} 获取采样开始时间: {ret}")
|
|
611
|
+
except Exception as e:
|
|
612
|
+
logger.error(f"ar4 {self._box_mac} 获取采样开始时间异常: {str(e)}")
|
|
613
|
+
|
|
614
|
+
def _wrap_data_accept(self):
|
|
615
|
+
|
|
616
|
+
@FuncAr4DataNotify
|
|
617
|
+
def data_accept(handle, data_ptr):
|
|
618
|
+
self._data_accept(data_ptr)
|
|
619
|
+
|
|
620
|
+
return data_accept
|
|
621
|
+
def _data_accept(self, data_ptr):
|
|
622
|
+
self.eeg_accept(LMPacket().transfer(data_ptr.contents))
|
|
623
|
+
|
|
624
|
+
def eeg_accept(self, packet: LMPacket):
|
|
625
|
+
pass
|
|
626
|
+
def eeg2phy(self, digtal):
|
|
627
|
+
# 向量化计算(自动支持广播)
|
|
628
|
+
return ((digtal - self._eeg_dig_min) / self._eeg_dig_range) * self._eeg_phy_range + self._eeg_phy_min
|
|
629
|
+
|
|
630
|
+
def acc2phy(self, digtal):
|
|
631
|
+
return ((digtal - self._acc_dig_min) / self._acc_dig_range) * self._acc_phy_range + self._acc_phy_min
|
|
632
|
+
def _wrap_trigger_accept(self):
|
|
633
|
+
|
|
634
|
+
@FuncAr4DataNotify
|
|
635
|
+
def trigger_accept(handle, time_ms, trigger_type, trigger_value):
|
|
636
|
+
self._trigger_accept(time_ms, trigger_type, trigger_value)
|
|
637
|
+
|
|
638
|
+
return trigger_accept
|
|
639
|
+
|
|
640
|
+
def _trigger_accept(self, time_ms, trigger_type, trigger_value):
|
|
641
|
+
logger.info(f"_trigger_accept 被调用")
|
|
642
|
+
logger.info(f"触发时间: {time_ms}, 触发类型: {trigger_type}, 触发值: {trigger_value}")
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _wrap_connected_notify(self):
|
|
646
|
+
|
|
647
|
+
@FuncAr4RecorderConnected
|
|
648
|
+
def connected_notify(handle):
|
|
649
|
+
self._connected_notify(handle)
|
|
650
|
+
|
|
651
|
+
return connected_notify
|
|
652
|
+
|
|
653
|
+
def _connected_notify(self, handle):
|
|
654
|
+
logger.info(f"_connected_notify 被调用 handle: {handle}")
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def _wrap_disconnected_notify(self):
|
|
658
|
+
@FuncAr4RecorderDisconnected
|
|
659
|
+
def disconnected_notify(handle):
|
|
660
|
+
self._disconnected_notify(handle)
|
|
661
|
+
|
|
662
|
+
return disconnected_notify
|
|
663
|
+
|
|
664
|
+
def _disconnected_notify(self, handle):
|
|
665
|
+
logger.info(f"_disconnected_notify 被调用 handle: {handle}")
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def _wrap_box_connected_notify(self):
|
|
669
|
+
|
|
670
|
+
@FuncAr4RecorderConnected
|
|
671
|
+
def box_connected_notify(handle):
|
|
672
|
+
self._box_connected_notify(handle)
|
|
673
|
+
|
|
674
|
+
return box_connected_notify
|
|
675
|
+
|
|
676
|
+
def _box_connected_notify(self, handle):
|
|
677
|
+
logger.info(f"_box_connected_notify 被调用 handle: {handle}")
|
|
678
|
+
|
|
679
|
+
def _wrap_box_disconnected_notify(self):
|
|
680
|
+
@FuncAr4RecorderDisconnected
|
|
681
|
+
def box_disconnected_notify(handle):
|
|
682
|
+
self._box_disconnected_notify(handle)
|
|
683
|
+
|
|
684
|
+
return box_disconnected_notify
|
|
685
|
+
|
|
686
|
+
def _box_disconnected_notify(self, handle):
|
|
687
|
+
logger.info(f"_box_disconnected_notify 被调用 handle: {handle}")
|
|
688
|
+
|
|
689
|
+
def _wrap_start_record_notify(self):
|
|
690
|
+
|
|
691
|
+
@FuncAr4RecorderConnected
|
|
692
|
+
def start_record_notify(handle):
|
|
693
|
+
self._start_record_notify(handle)
|
|
694
|
+
|
|
695
|
+
return start_record_notify
|
|
696
|
+
|
|
697
|
+
def _start_record_notify(self, handle):
|
|
698
|
+
logger.info(f"_start_record_notify 被调用 handle: {handle}")
|
|
699
|
+
self._recording = True
|
|
700
|
+
self._record_start_time = time()
|
|
701
|
+
logger.info(self)
|
|
702
|
+
|
|
703
|
+
def _wrap_stop_record_notify(self):
|
|
704
|
+
@FuncAr4RecorderDisconnected
|
|
705
|
+
def stop_record_notify(handle):
|
|
706
|
+
self._stop_record_notify(handle)
|
|
707
|
+
|
|
708
|
+
return stop_record_notify
|
|
709
|
+
|
|
710
|
+
def _stop_record_notify(self, handle):
|
|
711
|
+
logger.info(f"_stop_record_notify 被调用 handle: {handle}")
|
|
712
|
+
self._recording = False
|
|
713
|
+
|
|
714
|
+
def __str__(self):
|
|
715
|
+
return f"""
|
|
716
|
+
box mac: {self._box_mac},
|
|
717
|
+
box name: {self._box_name},
|
|
718
|
+
box soc: {self._box_soc}
|
|
719
|
+
head conn state: {self._head_conn_state}
|
|
720
|
+
head mac: {self._head_mac},
|
|
721
|
+
head soc: {self._head_soc}
|
|
722
|
+
connected: {self._connected}
|
|
723
|
+
connect time: {self._conn_time}
|
|
724
|
+
last time: {self._last_time}
|
|
725
|
+
[
|
|
726
|
+
eeg phy max: {self._eeg_phy_max}
|
|
727
|
+
eeg phy min: {self._eeg_phy_min}
|
|
728
|
+
eeg dig max: {self._eeg_dig_max}
|
|
729
|
+
eeg dig min: {self._eeg_dig_min}
|
|
730
|
+
eeg phy unit: {self._eeg_phy_unit}
|
|
731
|
+
]
|
|
732
|
+
[
|
|
733
|
+
acc phy max: {self._acc_phy_max}
|
|
734
|
+
acc phy min: {self._acc_phy_min}
|
|
735
|
+
acc dig max: {self._acc_dig_max}
|
|
736
|
+
acc dig min: {self._acc_dig_min}
|
|
737
|
+
acc phy unit: {self._acc_phy_unit}
|
|
738
|
+
]
|
|
739
|
+
dig -> ((dig - {self._eeg_dig_min}) / {self._eeg_dig_range}) * {self._eeg_phy_range} + {self._eeg_phy_min}
|
|
740
|
+
dig -> ((dig - {self._acc_dig_min}) / {self._acc_dig_range}) * {self._acc_phy_range} + {self._acc_phy_min}
|
|
741
|
+
"""
|
|
742
|
+
|