qlsdk2 0.5.0__tar.gz → 0.5.1.1__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 (74) hide show
  1. qlsdk2-0.5.1.1/PKG-INFO +62 -0
  2. qlsdk2-0.5.1.1/README.md +43 -0
  3. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/setup.py +2 -3
  4. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/entity/__init__.py +8 -12
  5. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/persist/rsc_edf.py +22 -8
  6. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/command/__init__.py +6 -2
  7. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/base.py +13 -2
  8. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/c16_rs.py +51 -71
  9. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/device_factory.py +1 -1
  10. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/interface/device.py +11 -1
  11. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/manager/container.py +13 -8
  12. qlsdk2-0.5.1.1/src/qlsdk/rsc/parser/base-new.py +135 -0
  13. qlsdk2-0.5.1.1/src/qlsdk/rsc/parser/base.py +157 -0
  14. qlsdk2-0.5.1.1/src/qlsdk2.egg-info/PKG-INFO +62 -0
  15. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk2.egg-info/SOURCES.txt +1 -0
  16. qlsdk2-0.5.0/PKG-INFO +0 -40
  17. qlsdk2-0.5.0/README.md +0 -12
  18. qlsdk2-0.5.0/src/qlsdk/rsc/parser/base.py +0 -150
  19. qlsdk2-0.5.0/src/qlsdk2.egg-info/PKG-INFO +0 -40
  20. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/setup.cfg +0 -0
  21. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/__init__.py +0 -0
  22. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/ar4/__init__.py +0 -0
  23. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/ar4m/__init__.py +0 -0
  24. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/__init__.py +0 -0
  25. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/crc/__init__.py +0 -0
  26. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/crc/crctools.py +0 -0
  27. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/device.py +0 -0
  28. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/exception.py +0 -0
  29. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/filter/__init__.py +0 -0
  30. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/filter/norch.py +0 -0
  31. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/local.py +0 -0
  32. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/message/__init__.py +0 -0
  33. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/message/command.py +0 -0
  34. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/message/tcp.py +0 -0
  35. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/message/udp.py +0 -0
  36. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/network/__init__.py +0 -0
  37. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/network/monitor.py +0 -0
  38. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/core/utils.py +0 -0
  39. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/persist/__init__.py +0 -0
  40. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/persist/ars_edf.py +0 -0
  41. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/persist/edf.py +0 -0
  42. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/persist/stream.py +0 -0
  43. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/__init__.py +0 -0
  44. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/__init__.py +0 -0
  45. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/arskindling.py +0 -0
  46. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/c256_rs.py +0 -0
  47. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/c64_rs.py +0 -0
  48. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/device/c64s1.py +0 -0
  49. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/eegion.py +0 -0
  50. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/entity.py +0 -0
  51. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/interface/__init__.py +0 -0
  52. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/interface/command.py +0 -0
  53. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/interface/handler.py +0 -0
  54. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/interface/parser.py +0 -0
  55. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/manager/__init__.py +0 -0
  56. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/manager/search.py +0 -0
  57. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/network/__init__.py +0 -0
  58. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/network/discover.py +0 -0
  59. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/paradigm.py +0 -0
  60. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/parser/__init__.py +0 -0
  61. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/rsc/proxy.py +0 -0
  62. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/sdk/__init__.py +0 -0
  63. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/sdk/ar4sdk.py +0 -0
  64. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/sdk/hub.py +0 -0
  65. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/sdk/libs/libAr4SDK.dll +0 -0
  66. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/sdk/libs/libwinpthread-1.dll +0 -0
  67. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/x8/__init__.py +0 -0
  68. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk/x8m/__init__.py +0 -0
  69. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk2.egg-info/dependency_links.txt +0 -0
  70. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk2.egg-info/requires.txt +0 -0
  71. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/src/qlsdk2.egg-info/top_level.txt +0 -0
  72. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/test/test.222.py +0 -0
  73. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/test/test.py +0 -0
  74. {qlsdk2-0.5.0 → qlsdk2-0.5.1.1}/test/test_ar4m.py +0 -0
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.1
2
+ Name: qlsdk2
3
+ Version: 0.5.1.1
4
+ Summary: SDK for quanlan device
5
+ Home-page: https://github.com/hehuajun/qlsdk
6
+ Author: hehuajun
7
+ Author-email: hehuajun@eegion.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 10
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: loguru>=0.6.0
14
+ Requires-Dist: numpy>=1.23.5
15
+ Requires-Dist: bitarray>=1.5.3
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=6.0; extra == "dev"
18
+ Requires-Dist: twine>=3.0; extra == "dev"
19
+
20
+ ## **v0.5.1.1** (2025-08-24)
21
+ #### 🐛 Bug Fixed
22
+ - 修复仅选择一个通道时,自动保存功能异常的问题
23
+
24
+
25
+ ## **v0.5.1** (2025-08-04)
26
+
27
+ #### ⚙️ Changed
28
+ - C16R通道映射调整为按老的脑电帽位点映射(固件端做了映射)
29
+
30
+ - 开放底层参数,支持EDF/BDF文件中存储大量事件(>1个/秒)
31
+
32
+ - 优化实时信号数据订阅(发布/订阅模式),在调用者使用不规范时,多个订阅者之间不会互相干扰
33
+
34
+
35
+ ## **v0.5.0** (2025-07-29)
36
+
37
+ #### 🚀 Added
38
+
39
+ - *新增C16R搜索与连接*
40
+ 支持C16R类型设备的搜索与连接
41
+
42
+ - *C16R信号采集/停止控制*
43
+ 支持信号采集的参数配置
44
+ 支持信号采集的启动与停止控制
45
+
46
+ - *C16R数据自动记录*
47
+ 采集到的信号数据自动保存为bdf文件
48
+
49
+ - *C16R采集通道设置*
50
+ - 支持数字模式通道配置
51
+ - 支持名称模式通道配置
52
+ - 支持两种模式混合使用
53
+
54
+ #### ⚙️ Changed
55
+
56
+ - *性能提升*
57
+ - 信号接收效率优化
58
+ - 指令拆包方式优化
59
+
60
+ - *日志系统改进*
61
+ - 日志级别精细化调整,减少不必要的日志信息
62
+ - 日志文案清晰化
@@ -0,0 +1,43 @@
1
+ ## **v0.5.1.1** (2025-08-24)
2
+ #### 🐛 Bug Fixed
3
+ - 修复仅选择一个通道时,自动保存功能异常的问题
4
+
5
+
6
+ ## **v0.5.1** (2025-08-04)
7
+
8
+ #### ⚙️ Changed
9
+ - C16R通道映射调整为按老的脑电帽位点映射(固件端做了映射)
10
+
11
+ - 开放底层参数,支持EDF/BDF文件中存储大量事件(>1个/秒)
12
+
13
+ - 优化实时信号数据订阅(发布/订阅模式),在调用者使用不规范时,多个订阅者之间不会互相干扰
14
+
15
+
16
+ ## **v0.5.0** (2025-07-29)
17
+
18
+ #### 🚀 Added
19
+
20
+ - *新增C16R搜索与连接*
21
+ 支持C16R类型设备的搜索与连接
22
+
23
+ - *C16R信号采集/停止控制*
24
+ 支持信号采集的参数配置
25
+ 支持信号采集的启动与停止控制
26
+
27
+ - *C16R数据自动记录*
28
+ 采集到的信号数据自动保存为bdf文件
29
+
30
+ - *C16R采集通道设置*
31
+ - 支持数字模式通道配置
32
+ - 支持名称模式通道配置
33
+ - 支持两种模式混合使用
34
+
35
+ #### ⚙️ Changed
36
+
37
+ - *性能提升*
38
+ - 信号接收效率优化
39
+ - 指令拆包方式优化
40
+
41
+ - *日志系统改进*
42
+ - 日志级别精细化调整,减少不必要的日志信息
43
+ - 日志文案清晰化
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8' ) as fh:
6
6
 
7
7
  setuptools.setup(
8
8
  name="qlsdk2",
9
- version="0.5.0",
9
+ version="0.5.1.1",
10
10
  author="hehuajun",
11
11
  author_email="hehuajun@eegion.com",
12
12
  description="SDK for quanlan device",
@@ -18,13 +18,12 @@ setuptools.setup(
18
18
  classifiers=[
19
19
  "Programming Language :: Python :: 3",
20
20
  "License :: OSI Approved :: MIT License",
21
- "Operating System :: OS Independent",
21
+ "Operating System :: Microsoft :: Windows :: Windows 10",
22
22
  ],
23
23
  python_requires='>=3.9',
24
24
  install_requires=open("requirements.txt").read().splitlines(),
25
25
  include_package_data=True,
26
26
  package_data={
27
- # "qlsdk2": ["/**/*.dll"],
28
27
  "qlsdk": ["./**/*.dll"]
29
28
  }
30
29
  )
@@ -1,18 +1,17 @@
1
1
  from qlsdk.core.utils import to_channels
2
2
  from loguru import logger
3
3
 
4
- class DataPacket(object):
5
- def __init__(self, device_type, device_id, channels, data):
6
- self.data = data
7
- self.channels = None
8
-
9
-
10
- class RscPacket(object):
4
+ class Packet(object):
11
5
  def __init__(self):
12
6
  self.time_stamp = None
13
7
  self.pkg_id = None
14
8
  self.result = None
15
9
  self.channels = None
10
+
11
+
12
+ class RscPacket(Packet):
13
+ def __init__(self):
14
+ super().__init__()
16
15
  self.origin_sample_rate = None
17
16
  self.sample_rate = None
18
17
  self.sample_num = None
@@ -65,12 +64,9 @@ class RscPacket(object):
65
64
  eeg: {self.eeg}
66
65
  """
67
66
 
68
- class ImpedancePacket(object):
67
+ class ImpedancePacket(Packet):
69
68
  def __init__(self):
70
- self.time_stamp = None
71
- self.pkg_id = None
72
- self.result = None
73
- self.channels = None
69
+ super().__init__()
74
70
  self.data_len = None
75
71
  self.impedance = None
76
72
 
@@ -15,7 +15,8 @@ EDF_FILE_TYPE = {
15
15
  }
16
16
 
17
17
  class EDFStreamWriter(Thread):
18
- def __init__(self, channels, sample_frequency, physical_max, digital_min, file_type, file_path):
18
+ def __init__(self, channels, sample_frequency, physical_max, digital_min, file_type, file_path, record_duration=None):
19
+
19
20
  super().__init__()
20
21
  self._writer : EdfWriter = None
21
22
  self.data_queue : Queue = Queue()
@@ -23,6 +24,8 @@ class EDFStreamWriter(Thread):
23
24
  self._points = 0
24
25
  self._duration = 0
25
26
  self._buffer = None
27
+ # 设置edf/bdf文件参数,设置[0.001, 1)可以在1秒内记录多个事件(不建议开启)
28
+ self.record_duration = record_duration
26
29
 
27
30
  # signal info
28
31
  self._channels = channels
@@ -62,9 +65,10 @@ class EDFStreamWriter(Thread):
62
65
  # 数据
63
66
  self.data_queue.put(data)
64
67
 
65
- def trigger(self, onset, desc):
68
+ def trigger(self, onset, desc: str):
66
69
  if self._writer:
67
- self._writer.writeAnnotation(onset, 1, desc)
70
+ logger.trace(f"[{onset} : {desc}]")
71
+ self._writer.writeAnnotation(onset, -1, desc)
68
72
  else:
69
73
  logger.warning("未创建文件,无法写入Trigger标记")
70
74
 
@@ -87,7 +91,7 @@ class EDFStreamWriter(Thread):
87
91
  logger.debug("收到结束信号,停止写入数据")
88
92
  break
89
93
  # 处理数据
90
- self._points += len(data[1])
94
+ self._points += len(data[0])
91
95
  logger.trace(f"已处理数据点数:{self._points}")
92
96
  self._write_file(data)
93
97
  # 有数据重置计数器
@@ -135,6 +139,8 @@ class EDFStreamWriter(Thread):
135
139
  })
136
140
 
137
141
  self._writer.setSignalHeaders(signal_headers)
142
+ if self.record_duration:
143
+ self._writer.setDatarecordDuration(self.record_duration) # 每个数据块1秒
138
144
 
139
145
  def _write_file(self, eeg_data):
140
146
  try:
@@ -189,7 +195,7 @@ class RscEDFHandler(object):
189
195
  @author: qlsdk
190
196
  @since: 0.4.0
191
197
  '''
192
- def __init__(self, eeg_sample_rate, physical_max, physical_min, resolution=24, storage_path = None):
198
+ def __init__(self, eeg_sample_rate, physical_max, physical_min, resolution=24, storage_path = None, record_duration=None):
193
199
  # edf文件参数
194
200
  self.physical_max = physical_max
195
201
  self.physical_min = physical_min
@@ -225,6 +231,7 @@ class RscEDFHandler(object):
225
231
  self._total_packets = 0
226
232
  self._lost_packets = 0
227
233
  self._storage_path = storage_path
234
+ self._record_duration = record_duration
228
235
  self._edf_writer_thread = None
229
236
  self._file_prefix = None
230
237
 
@@ -299,7 +306,7 @@ class RscEDFHandler(object):
299
306
  self._total_packets += 1
300
307
 
301
308
  if self._edf_writer_thread is None:
302
- self._edf_writer_thread = EDFStreamWriter(self.channels, self.sample_rate, self.physical_max, self.physical_min, self.file_type, self.file_name)
309
+ self._edf_writer_thread = EDFStreamWriter(self.channels, self.sample_rate, self.physical_max, self.physical_min, self.file_type, self.file_name, self._record_duration)
303
310
  self._edf_writer_thread.set_start_time(self._start_time)
304
311
  self._edf_writer_thread.start()
305
312
  logger.info(f"开始写入数据: {self.file_name}")
@@ -310,9 +317,16 @@ class RscEDFHandler(object):
310
317
  # trigger标记
311
318
  # desc: 标记内容
312
319
  # cur_time: 设备时间时间戳,非设备发出的trigger不要设置
313
- def trigger(self, desc, cur_time=None):
320
+ def trigger(self, desc: str, cur_time=None):
321
+ if self._edf_writer_thread is None:
322
+ logger.warning(f"File writing has not started, discarding trigger {desc}")
323
+ return
324
+
314
325
  if cur_time is None:
315
- onset = datetime.now().timestamp() - self._start_time.timestamp()
326
+ # 计算trigger位置
327
+ if self._start_time:
328
+ onset = datetime.now().timestamp() - self._start_time.timestamp()
329
+ else: onset = 0
316
330
  else:
317
331
  onset = cur_time - self._first_timestamp
318
332
  self._edf_writer_thread.trigger(onset, desc)
@@ -170,7 +170,7 @@ class QueryBatteryCommand(DeviceCommand):
170
170
  self.device.battery_total = body[12]
171
171
  # state - 1b
172
172
  # state = body[13]
173
- logger.debug(f"电量更新: {self.device}")
173
+ logger.trace(f"电量更新: {self.device}")
174
174
  else:
175
175
  logger.warning(f"QueryBatteryCommand message received but result is failed.")
176
176
 
@@ -214,6 +214,10 @@ class StartImpedanceCommand(DeviceCommand):
214
214
  body += to_bytes(self.device.acq_channels)
215
215
  body += bytes.fromhex('0000000000000000') # 8字节占位符
216
216
  return body
217
+
218
+ def parse_body(self, body):
219
+ logger.info(f"Received StartImpedance response: {body.hex()}")
220
+ return super().parse_body(body)
217
221
 
218
222
 
219
223
  # 停止阻抗测量
@@ -293,7 +297,7 @@ class ImpedanceDataCommand(DeviceCommand):
293
297
  cmd_desc = "阻抗数据"
294
298
 
295
299
  def parse_body(self, body: bytes):
296
- logger.info(f"Received impedance data: {body.hex()}")
300
+ # logger.info(f"Received impedance data: {body.hex()}")
297
301
  packet = ImpedancePacket().transfer(body)
298
302
 
299
303
  # 信号数据
@@ -20,6 +20,9 @@ class QLBaseDevice(IDevice):
20
20
  # 启动数据解析线程
21
21
  self._parser: IParser = None
22
22
 
23
+ # 用在edf/bdf文件写入中,不建议使用
24
+ self._record_duration = None
25
+
23
26
  # 设备信息
24
27
  self.device_id = None
25
28
  self.device_name = None
@@ -130,8 +133,11 @@ class QLBaseDevice(IDevice):
130
133
  def parser(self) -> IParser:
131
134
  return self._parser
132
135
 
136
+ def set_record_duration(self, record_duration: float):
137
+ self._record_duration = record_duration
138
+
133
139
  # 数据包处理
134
- def produce(self, data: RscPacket):
140
+ def produce(self, data: RscPacket, type:Literal['signal', 'impedance']="signal"):
135
141
  if data is None: return
136
142
 
137
143
  # 处理信号数据
@@ -147,6 +153,10 @@ class QLBaseDevice(IDevice):
147
153
 
148
154
  # 发送数据包到订阅者
149
155
  for q in list(self.signal_consumers.values()):
156
+ # 队列满了就丢弃最早的数据
157
+ if q.full():
158
+ q.get()
159
+
150
160
  q.put(data)
151
161
 
152
162
  # 信号数据转换(默认不处理)
@@ -176,7 +186,8 @@ class QLBaseDevice(IDevice):
176
186
 
177
187
  def stop_listening(self):
178
188
  logger.trace(f"设备{self.device_no}停止socket监听")
179
- self._listening = False
189
+ self._listening = False
190
+ self._parser.stop()
180
191
 
181
192
  @property
182
193
  def device_type(self) -> int:
@@ -23,7 +23,8 @@ class C16RS(QLBaseDevice):
23
23
 
24
24
  super().__init__(socket)
25
25
  # 存储通道反向映射位置值
26
- self._reverse_ch_pos = None
26
+ self._reverse_ch_pos = None
27
+
27
28
  self.channel_name_mapping = {
28
29
  "FP1": 1,
29
30
  "FP2": 2,
@@ -45,79 +46,59 @@ class C16RS(QLBaseDevice):
45
46
  "P3": 18,
46
47
  "P7": 19,
47
48
  "P4": 20,
48
- "P8": 21
49
+ "P8": 21,
50
+ "T3": 8,
51
+ "T4": 9,
52
+ "A1": 10,
53
+ "A2": 11,
49
54
  }
50
55
 
51
- # self.channel_mapping = {
52
- # "1": 61,
53
- # "2": 62,
54
- # "3": 63,
55
- # "4": 64,
56
- # "5": 65,
57
- # "6": 66,
58
- # "7": 68,
59
- # "8": 67,
60
- # "9": 53,
61
- # "10": 54,
62
- # "11": 55,
63
- # "12": 57,
64
- # "13": 59,
65
- # "14": 58,
66
- # "15": 60,
67
- # "16": 56,
68
- # "17": 26,
69
- # "18": 25,
70
- # "19": 24,
71
- # "20": 23,
72
- # "21": 22
73
- # }
74
-
75
56
  self.channel_mapping = {
76
- "1": 2,
77
- "2": 3,
78
- "3": 27,
79
- "4": 31,
80
- "5": 60,
81
- "6": 61,
82
- "7": 25,
83
- "8": 29,
84
- "9": 33,
85
- "10": 62,
86
- "11": 63,
87
- "12": 10,
88
- "13": 14,
89
- "14": 8,
90
- "15": 12,
91
- "16": 16,
92
- "17": 43,
93
- "18": 45,
94
- "19": 47,
95
- "20": 49,
96
- "21": 51
57
+ "1": 59,
58
+ "2": 60,
59
+ "3": 53,
60
+ "4": 50,
61
+ "5": 51,
62
+ "6": 64,
63
+ "7": 49,
64
+ "8": 63,
65
+ "9": 24,
66
+ "10": 14,
67
+ "11": 10,
68
+ "12": 11,
69
+ "13": 57,
70
+ "14": 16,
71
+ "15": 55,
72
+ "16": 15,
73
+ "17": 40,
74
+ "18": 37,
75
+ "19": 34,
76
+ "20": 18,
77
+ "21": 47
97
78
  }
98
79
 
99
80
  self.channel_display_mapping = {
100
- 2: 1,
101
- 3: 2,
102
- 27: 3,
103
- 31: 4,
104
- 60: 5,
105
- 61: 6,
106
- 25: 7,
107
- 29: 8,
108
- 33: 9,
109
- 62: 10,
110
- 63: 11,
111
- 10: 12,
112
- 14: 13,
113
- 8: 14,
114
- 12: 15,
115
- 16: 16,
116
- 43: 17,
117
- 45: 18,
118
- 47: 19,
119
- 49: 20,
120
- 51: 21
81
+ 59: 1,
82
+ 60: 2,
83
+ 53: 3,
84
+ 50: 4,
85
+ 51: 5,
86
+ 64: 6,
87
+ 49: 7,
88
+ 63: 8,
89
+ 24: 9,
90
+ 14: 10,
91
+ 10: 11,
92
+ 11: 12,
93
+ 57: 13,
94
+ 16: 14,
95
+ 55: 15,
96
+ 15: 16,
97
+ 40: 17,
98
+ 37: 18,
99
+ 34: 19,
100
+ 18: 20,
101
+ 47: 21
121
102
  }
122
103
 
123
104
 
@@ -126,11 +107,10 @@ class C16RS(QLBaseDevice):
126
107
  rlt = cls(parent.socket)
127
108
  rlt.device_id = parent.device_id
128
109
  rlt._device_no = parent.device_no
129
- return rlt
130
-
110
+ return rlt
131
111
 
132
112
  def init_edf_handler(self):
133
- self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
113
+ self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution, record_duration = self._record_duration)
134
114
  self._edf_handler.set_device_type(self.device_type)
135
115
  self._edf_handler.set_device_no(self.device_no)
136
116
  self._edf_handler.set_storage_path(self._storage_path)
@@ -16,7 +16,7 @@ class DeviceFactory(object):
16
16
 
17
17
  @classmethod
18
18
  def create_device(cls, device: IDevice) -> Type[IDevice]:
19
- logger.debug(f"Creating device for device type: {hex(device.device_type)}, support types: {cls._devices.keys()}")
19
+ logger.trace(f"Creating device for device type: {hex(device.device_type)}, support types: {cls._devices.keys()}")
20
20
  if device.device_type not in cls._devices.keys():
21
21
  logger.warning(f"不支持的设备类型: {hex(device.device_type)}")
22
22
  raise ValueError(f"Unsupported device type: {hex(device.device_type)}")
@@ -1,5 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
  import abc
3
+ from typing import Literal
3
4
 
4
5
  class IDevice(ABC):
5
6
 
@@ -11,11 +12,20 @@ class IDevice(ABC):
11
12
  def set_device_type(self, value: int):
12
13
  pass
13
14
 
15
+ def set_storage_path(self, path: str):
16
+ pass
17
+
18
+ def set_file_prefix(self, pre: str):
19
+ pass
20
+
21
+ def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
22
+ pass
23
+
14
24
  @property
15
25
  def device_no(self) -> str:
16
26
  pass
17
27
 
18
- def produce(self, data) -> None:
28
+ def produce(self, data, type:Literal['signal', 'impedance']="signal") -> None:
19
29
  pass
20
30
 
21
31
  def start_listening(self):
@@ -50,7 +50,7 @@ class DeviceContainer(object):
50
50
  try:
51
51
  # 绑定到所有接口的19216端口
52
52
  tcp_socket.bind(('0.0.0.0', self._tcp_port))
53
- tcp_socket.listen(5)
53
+ tcp_socket.listen(100)
54
54
  logger.info(f"端口[{self._tcp_port}]监听服务已启动")
55
55
 
56
56
  while True:
@@ -59,16 +59,21 @@ class DeviceContainer(object):
59
59
 
60
60
 
61
61
  # 为每个新连接创建线程处理
62
- client_handler = Thread(
63
- target=self.client_handler,
64
- args=(client_socket,),
65
- daemon=True
66
- )
67
-
68
- client_handler.start()
62
+ try:
63
+ client_handler = Thread(
64
+ target=self.client_handler,
65
+ args=(client_socket,),
66
+ daemon=False
67
+ )
68
+
69
+ client_handler.start()
70
+ except Exception as e:
71
+ logger.error(f"Handler Error: {e}")
69
72
 
70
73
  except KeyboardInterrupt:
71
74
  logger.error(f"端口[{self._tcp_port}]监听服务异常关闭")
75
+ except Exception as e:
76
+ logger.error(f"端口[{self._tcp_port}]监听服务异常: {e}")
72
77
  finally:
73
78
  logger.error(f"端口[{self._tcp_port}]监听服务关闭")
74
79
  tcp_socket.close()