qlsdk2 0.2.0__py3-none-any.whl → 0.3.0a1__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/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
+