qlsdk2 0.5.1.1__tar.gz → 0.6.0a1__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 (84) hide show
  1. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/PKG-INFO +11 -2
  2. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/setup.py +1 -1
  3. qlsdk2-0.6.0a1/src/qlsdk/core/entity/__init__.py +164 -0
  4. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/message/udp.py +9 -1
  5. qlsdk2-0.6.0a1/src/qlsdk/entity/message.py +0 -0
  6. qlsdk2-0.6.0a1/src/qlsdk/entity/signal.py +0 -0
  7. qlsdk2-0.6.0a1/src/qlsdk/interface/__init__.py +10 -0
  8. qlsdk2-0.6.0a1/src/qlsdk/interface/analyzer.py +2 -0
  9. qlsdk2-0.6.0a1/src/qlsdk/interface/collector.py +10 -0
  10. qlsdk2-0.6.0a1/src/qlsdk/interface/device.py +2 -0
  11. qlsdk2-0.6.0a1/src/qlsdk/interface/parser.py +13 -0
  12. qlsdk2-0.6.0a1/src/qlsdk/interface/stimulator.py +2 -0
  13. qlsdk2-0.6.0a1/src/qlsdk/interface/store.py +2 -0
  14. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/persist/rsc_edf.py +2 -0
  15. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/command/__init__.py +4 -4
  16. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/__init__.py +2 -1
  17. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/base.py +60 -27
  18. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/c256_rs.py +25 -32
  19. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/device_factory.py +1 -0
  20. qlsdk2-0.6.0a1/src/qlsdk/rsc/interface/device.py +64 -0
  21. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/interface/parser.py +6 -0
  22. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/manager/container.py +10 -2
  23. qlsdk2-0.6.0a1/src/qlsdk/rsc/manager/search.py +0 -0
  24. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/network/discover.py +2 -0
  25. qlsdk2-0.6.0a1/src/qlsdk/rsc/parser/__init__.py +2 -0
  26. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/parser/base.py +1 -1
  27. qlsdk2-0.6.0a1/src/qlsdk/rsc/parser/rsc.py +130 -0
  28. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk2.egg-info/PKG-INFO +11 -2
  29. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk2.egg-info/SOURCES.txt +11 -0
  30. qlsdk2-0.5.1.1/src/qlsdk/core/entity/__init__.py +0 -89
  31. qlsdk2-0.5.1.1/src/qlsdk/rsc/interface/device.py +0 -127
  32. qlsdk2-0.5.1.1/src/qlsdk/rsc/parser/__init__.py +0 -1
  33. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/README.md +0 -0
  34. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/setup.cfg +0 -0
  35. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/__init__.py +0 -0
  36. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/ar4/__init__.py +0 -0
  37. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/ar4m/__init__.py +0 -0
  38. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/__init__.py +0 -0
  39. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/crc/__init__.py +0 -0
  40. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/crc/crctools.py +0 -0
  41. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/device.py +0 -0
  42. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/exception.py +0 -0
  43. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/filter/__init__.py +0 -0
  44. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/filter/norch.py +0 -0
  45. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/local.py +0 -0
  46. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/message/__init__.py +0 -0
  47. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/message/command.py +0 -0
  48. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/message/tcp.py +0 -0
  49. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/network/__init__.py +0 -0
  50. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/network/monitor.py +0 -0
  51. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/core/utils.py +0 -0
  52. /qlsdk2-0.5.1.1/src/qlsdk/rsc/manager/search.py → /qlsdk2-0.6.0a1/src/qlsdk/entity/__init__.py +0 -0
  53. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/persist/__init__.py +0 -0
  54. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/persist/ars_edf.py +0 -0
  55. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/persist/edf.py +0 -0
  56. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/persist/stream.py +0 -0
  57. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/__init__.py +0 -0
  58. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/arskindling.py +0 -0
  59. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/c16_rs.py +0 -0
  60. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/c64_rs.py +0 -0
  61. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/device/c64s1.py +0 -0
  62. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/eegion.py +0 -0
  63. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/entity.py +0 -0
  64. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/interface/__init__.py +0 -0
  65. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/interface/command.py +0 -0
  66. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/interface/handler.py +0 -0
  67. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/manager/__init__.py +0 -0
  68. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/network/__init__.py +0 -0
  69. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/paradigm.py +0 -0
  70. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/parser/base-new.py +0 -0
  71. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/rsc/proxy.py +0 -0
  72. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/sdk/__init__.py +0 -0
  73. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/sdk/ar4sdk.py +0 -0
  74. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/sdk/hub.py +0 -0
  75. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/sdk/libs/libAr4SDK.dll +0 -0
  76. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/sdk/libs/libwinpthread-1.dll +0 -0
  77. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/x8/__init__.py +0 -0
  78. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk/x8m/__init__.py +0 -0
  79. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk2.egg-info/dependency_links.txt +0 -0
  80. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk2.egg-info/requires.txt +0 -0
  81. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/src/qlsdk2.egg-info/top_level.txt +0 -0
  82. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/test/test.222.py +0 -0
  83. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/test/test.py +0 -0
  84. {qlsdk2-0.5.1.1 → qlsdk2-0.6.0a1}/test/test_ar4m.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: qlsdk2
3
- Version: 0.5.1.1
3
+ Version: 0.6.0a1
4
4
  Summary: SDK for quanlan device
5
5
  Home-page: https://github.com/hehuajun/qlsdk
6
6
  Author: hehuajun
@@ -16,6 +16,15 @@ Requires-Dist: bitarray>=1.5.3
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: pytest>=6.0; extra == "dev"
18
18
  Requires-Dist: twine>=3.0; extra == "dev"
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
19
28
 
20
29
  ## **v0.5.1.1** (2025-08-24)
21
30
  #### 🐛 Bug Fixed
@@ -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.1.1",
9
+ version="0.6.0a1",
10
10
  author="hehuajun",
11
11
  author_email="hehuajun@eegion.com",
12
12
  description="SDK for quanlan device",
@@ -0,0 +1,164 @@
1
+ from qlsdk.core.utils import to_channels
2
+ from loguru import logger
3
+
4
+ class Packet(object):
5
+ def __init__(self):
6
+ self.time_stamp = None
7
+ self.pkg_id = None
8
+ self.result = None
9
+ self.channels = None
10
+
11
+
12
+ class RscPacket(Packet):
13
+ def __init__(self):
14
+ super().__init__()
15
+ self.origin_sample_rate = None
16
+ self.sample_rate = None
17
+ self.sample_num = None
18
+ self.resolution = None
19
+ self.filter = None
20
+ self.data_len = None
21
+ self.trigger = None
22
+ self.eeg = None
23
+
24
+ @staticmethod
25
+ def transfer(body: bytes) -> 'RscPacket':
26
+ packet = RscPacket()
27
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
28
+ packet.result = body[8]
29
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
30
+ logger.trace(f"pkg_id: {packet.pkg_id}")
31
+ packet.channels = to_channels(body[13: 45])
32
+ packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
33
+ packet.sample_rate = int.from_bytes(body[49: 53], 'little')
34
+ packet.sample_num = int.from_bytes(body[53: 57], 'little')
35
+ packet.resolution = int(int(body[57]) / 8)
36
+ packet.filter = body[58]
37
+ packet.data_len = int.from_bytes(body[59: 63], 'little')
38
+ # 步径 相同通道的点间隔
39
+ step = int(len(packet.channels) * packet.resolution + 4)
40
+ b_eeg = body[63:]
41
+ ch_num = len(packet.channels)
42
+ # 字节序列(4Cn{channel_size}){sample_num})
43
+ packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
44
+
45
+ packet.eeg = [
46
+ [
47
+ int.from_bytes(b_eeg[i * packet.resolution + 4 + j * step:i * packet.resolution + 4 + j * step + 3], 'big', signed=True)
48
+ for j in range(packet.sample_num)
49
+ ]
50
+ for i in range(ch_num)
51
+ ]
52
+
53
+ # logger.trace(self)
54
+ return packet
55
+
56
+ def __str__(self):
57
+ return f"""
58
+ time_stamp: {self.time_stamp}
59
+ pkg_id: {self.pkg_id}
60
+ origin_sample_rate: {self.origin_sample_rate}
61
+ sample_rate: {self.sample_rate}
62
+ sample_num: {self.sample_num}
63
+ resolution: {self.resolution}
64
+ filter: {self.filter}
65
+ channels: {self.channels}
66
+ data len: {self.data_len}
67
+ trigger: {self.trigger}
68
+ eeg: {self.eeg}
69
+ """
70
+
71
+ class ImpedancePacket(Packet):
72
+ def __init__(self):
73
+ super().__init__()
74
+ self.data_len = None
75
+ self.impedance = None
76
+
77
+ def transfer(self, body:bytes) -> 'ImpedancePacket':
78
+ self.time_stamp = int.from_bytes(body[0:8], 'little')
79
+ self.result = body[8]
80
+ self.pkg_id = int.from_bytes(body[9: 13], 'little')
81
+ self.channels = to_channels(body[13: 45])
82
+
83
+ logger.debug(f"impedance: {self}")
84
+
85
+ def __str__(self):
86
+ return f"""
87
+ time_stamp: {self.time_stamp}
88
+ pkg_id: {self.pkg_id}
89
+ result: {self.result}
90
+ channels: {self.channels}
91
+ data len: {self.data_len}
92
+ impedance: {self.impedance}
93
+ """
94
+
95
+
96
+ class C256RSPacket(Packet):
97
+ def __init__(self):
98
+ super().__init__()
99
+ self.origin_sample_rate = None
100
+ self.sample_rate = None
101
+ self.sample_num = None
102
+ self.resolution = None
103
+ self.filter = None
104
+ self.data_len = None
105
+ self.trigger = None
106
+ self.eeg = None
107
+
108
+ @staticmethod
109
+ def transfer(body: bytes) -> 'RscPacket':
110
+ packet = RscPacket()
111
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
112
+ packet.result = body[8]
113
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
114
+ packet.channels = to_channels(body[13: 45])
115
+ logger.trace(f"pkg_id: {packet.pkg_id}, channels: {packet.channels}")
116
+
117
+ packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
118
+ packet.sample_rate = int.from_bytes(body[49: 53], 'little')
119
+ packet.sample_num = int.from_bytes(body[53: 57], 'little')
120
+ packet.resolution = int(int(body[57]) / 8)
121
+ packet.filter = body[58]
122
+ packet.data_len = int.from_bytes(body[59: 63], 'little')
123
+
124
+ # 数据块
125
+ b_eeg = body[63:]
126
+ # 根据值域分割数组-代表设备的4个模块
127
+ ranges = [(1, 64), (65, 128), (129, 192), (193, 256)]
128
+ sub_channels = [[x for x in packet.channels if low <= x <= high] for low, high in ranges]
129
+ # 只处理选中的模块
130
+ sub_channels = [_ for _ in sub_channels if len(_) > 0]
131
+ # 步径 相同通道的点间隔
132
+ step = int(len(packet.channels) * packet.resolution + 4 * len(sub_channels))
133
+ offset = 0
134
+
135
+ # 分按子模块处理
136
+ for channels in sub_channels:
137
+ logger.trace(f"子数组: {channels} 长度: {len(channels)}")
138
+ channel_size = len(channels)
139
+
140
+ # 模块没有选中通道的,跳过
141
+ if channel_size == 0:
142
+ continue
143
+
144
+ # 只保留第一个有效模块的trigger,其他的模块是冗余信息,无实际含义
145
+ if packet.trigger is None:
146
+ packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
147
+ logger.trace(f"trigger: {packet.trigger}")
148
+ trigger_positions = [index for index, value in enumerate(packet.trigger) if value != 0]
149
+ if len(trigger_positions) > 0:
150
+ logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[packet.time_stamp + int(pos * 1000 / packet.sample_rate) for pos in trigger_positions]}")
151
+
152
+ eeg = [
153
+ [
154
+ int.from_bytes(b_eeg[offset + step * j + 4 + k * packet.resolution : offset + step * j + 7 + k * packet.resolution], 'big', signed=True)
155
+ for j in range(packet.sample_num)
156
+ ]
157
+ for k in range(channel_size)
158
+ ]
159
+ packet.eeg = packet.eeg + eeg if packet.eeg else eeg
160
+
161
+ offset += 4 + channel_size * packet.resolution
162
+
163
+ logger.trace(packet)
164
+ return packet
@@ -2,7 +2,7 @@
2
2
  from enum import Enum
3
3
  from time import timezone
4
4
  from qlsdk.core.crc import check_crc, crc16
5
- from qlsdk.core.local import get_ip
5
+ from qlsdk.core.local import get_ip, get_ips
6
6
 
7
7
  from loguru import logger
8
8
 
@@ -72,6 +72,14 @@ class UDPMessage(object):
72
72
  if server_ip is None:
73
73
  server_ip = get_ip()
74
74
 
75
+ return UDPMessage._search_(device_id, server_ip, server_port)
76
+
77
+ @staticmethod
78
+ def _search_(device_id : str, server_ip : str, server_port : int=19216):
79
+ # 服务端Ip
80
+ if server_ip is None:
81
+ raise ValueError("server_ip is None")
82
+
75
83
  logger.debug(f"search device {device_id} on {server_ip}:{server_port}")
76
84
 
77
85
  message = bytearray(28)
File without changes
File without changes
@@ -0,0 +1,10 @@
1
+ from parser import *
2
+
3
+ from device import *
4
+
5
+
6
+ '''
7
+ 设备消息解析器
8
+ '''
9
+ class DeviceMessageParser:
10
+ pass
@@ -0,0 +1,2 @@
1
+ class IAnalyzer:
2
+ pass
@@ -0,0 +1,10 @@
1
+ class ICollector:
2
+ def __init__(self):
3
+ pass
4
+
5
+ def collect_data(self):
6
+ pass
7
+
8
+ class Collector(ICollector):
9
+ def __init__(self):
10
+ super().__init__()
@@ -0,0 +1,2 @@
1
+ class IDevice:
2
+ pass
@@ -0,0 +1,13 @@
1
+ class IParser:
2
+ pass
3
+
4
+ class Parser(IParser):
5
+ def __init__(self):
6
+ super().__init__()
7
+
8
+ class MessageParser(Parser):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ def parse(self, data):
13
+ pass
@@ -0,0 +1,2 @@
1
+ class IStimulator:
2
+ pass
@@ -0,0 +1,2 @@
1
+ class IStore:
2
+ pass
@@ -260,6 +260,8 @@ class RscEDFHandler(object):
260
260
  self._device_type = "C64RS"
261
261
  elif device_type == 0x40:
262
262
  self._device_type = "LJ64S1"
263
+ elif device_type == 0x51:
264
+ self._device_type = "C256RS"
263
265
  elif device_type == 0x60:
264
266
  self._device_type = "ARSKindling"
265
267
  elif device_type == 0x339:
@@ -309,11 +309,11 @@ class SignalDataCommand(DeviceCommand):
309
309
  return super().unpack(payload)
310
310
 
311
311
  def parse_body(self, body: bytes):
312
- # 解析数据包
313
- packet = RscPacket()
314
- packet.transfer(body)
312
+ # # 解析数据包
313
+ # packet = RscPacket()
314
+ # packet.transfer(body)
315
315
  # 将数据包传递给设备
316
- self.device.produce(packet)
316
+ self.device.produce(body)
317
317
 
318
318
 
319
319
 
@@ -4,4 +4,5 @@ from .device_factory import DeviceFactory
4
4
  # import devices
5
5
  from .c64_rs import C64RS
6
6
  from .c16_rs import C16RS
7
- from .arskindling import ARSKindling
7
+ from .arskindling import ARSKindling
8
+ from .c256_rs import C256RS
@@ -1,4 +1,3 @@
1
-
2
1
  from multiprocessing import Queue
3
2
  from threading import Thread
4
3
  from time import sleep, time_ns
@@ -8,10 +7,9 @@ from loguru import logger
8
7
  import numpy as np
9
8
  from qlsdk.core.entity import RscPacket
10
9
  from qlsdk.core.utils import to_bytes
11
- from qlsdk.persist import RscEDFHandler
12
10
  from qlsdk.rsc.interface import IDevice, IParser
13
- from qlsdk.rsc.parser import TcpMessageParser
14
11
  from qlsdk.rsc.command import StartImpedanceCommand, StopImpedanceCommand, StartStimulationCommand, StopStimulationCommand, SetAcquisitionParamCommand, StartAcquisitionCommand, StopAcquisitionCommand
12
+ from qlsdk.rsc.parser.base import TcpMessageParser
15
13
 
16
14
  class QLBaseDevice(IDevice):
17
15
  def __init__(self, socket):
@@ -137,12 +135,18 @@ class QLBaseDevice(IDevice):
137
135
  self._record_duration = record_duration
138
136
 
139
137
  # 数据包处理
140
- def produce(self, data: RscPacket, type:Literal['signal', 'impedance']="signal"):
141
- if data is None: return
138
+ def produce(self, body: bytes, type:Literal['signal', 'impedance']="signal"):
139
+ if body is None: return
142
140
 
143
141
  # 处理信号数据
144
- self._signal_wrapper(data)
145
-
142
+ data = self._signal_wrapper(body)
143
+ # logger.debug("pkg_id: {}, eeg len: {}".format(data.pkg_id, len(data.eeg)))
144
+ #
145
+ trigger_positions = [index for index, value in enumerate(data.trigger) if value != 0]
146
+ if len(trigger_positions) > 0:
147
+ # logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[data.time_stamp + int(pos * 1000 / data.sample_rate) for pos in trigger_positions]}")
148
+ for pos in trigger_positions:
149
+ self.trigger(data.trigger[pos])
146
150
  # 存储
147
151
  if self.storage_enable:
148
152
  self._write_signal(data)
@@ -159,9 +163,9 @@ class QLBaseDevice(IDevice):
159
163
 
160
164
  q.put(data)
161
165
 
162
- # 信号数据转换(默认不处理)
163
- def _signal_wrapper(self, data: RscPacket):
164
- pass
166
+ # 信号数据转换
167
+ def _signal_wrapper(self, body: bytes):
168
+ return RscPacket().transfer(body)
165
169
 
166
170
  def _write_signal(self, data: RscPacket):
167
171
  # 文件写入到edf
@@ -175,8 +179,10 @@ class QLBaseDevice(IDevice):
175
179
  def start_listening(self):
176
180
 
177
181
  try:
182
+ # 启动消息解析器
178
183
  self.start_message_parser()
179
184
 
185
+ # 启动消息监听器
180
186
  self.start_message_listening()
181
187
  except Exception as e:
182
188
  logger.error(f"设备{self.device_no}准备失败: {str(e)}")
@@ -188,7 +194,22 @@ class QLBaseDevice(IDevice):
188
194
  logger.trace(f"设备{self.device_no}停止socket监听")
189
195
  self._listening = False
190
196
  self._parser.stop()
191
-
197
+
198
+ def read_msg(self, size: int) -> bytes:
199
+ try:
200
+ self.socket.settimeout(2.0)
201
+ return self.socket.recv(size)
202
+ except Exception as e:
203
+ logger.error(f"read_msg exception: {str(e)}")
204
+ raise ValueError("read_msg exception") from e
205
+
206
+ @classmethod
207
+ def from_parent(cls, parent:IDevice) -> IDevice:
208
+ rlt = cls(parent.socket)
209
+ rlt.device_id = parent.device_id
210
+ rlt._device_no = parent.device_no
211
+ return rlt
212
+
192
213
  @property
193
214
  def device_type(self) -> int:
194
215
  return self._device_type
@@ -196,18 +217,21 @@ class QLBaseDevice(IDevice):
196
217
  def start_message_parser(self) -> None:
197
218
  self._parser = TcpMessageParser(self)
198
219
  self._parser.start()
199
- logger.debug("TCP消息解析器已启动")
220
+ logger.debug("RSC消息解析器已启动")
200
221
 
201
222
  def start_message_listening(self) -> None:
202
223
  def _accept():
203
224
  while self._listening:
204
- # 缓冲区4M
205
- data = self.socket.recv(4096*1024)
206
- if not data:
207
- logger.warning(f"设备[{self.device_name}]连接结束")
225
+ try:
226
+ # 缓冲区4M
227
+ data = self.socket.recv(4096*1024)
228
+ if not data:
229
+ logger.warning(f"设备[{self.device_name}]连接结束")
230
+ break
231
+ self._parser.append(data)
232
+ except Exception as e:
233
+ logger.debug(f"设备[{self.device_name}]接收数据异常: {str(e)}")
208
234
  break
209
-
210
- self._parser.append(data)
211
235
 
212
236
  # 启动数据接收线程
213
237
  self._listening = True
@@ -252,11 +276,6 @@ class QLBaseDevice(IDevice):
252
276
  def init_edf_handler(self):
253
277
  logger.warning("init_edf_handler not implemented in base class, should be overridden in subclass")
254
278
  pass
255
- # self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
256
- # self._edf_handler.set_device_type(self.device_type)
257
- # self._edf_handler.set_device_no(self.device_no)
258
- # self._edf_handler.set_storage_path(self._storage_path)
259
- # self._edf_handler.set_file_prefix(self._file_prefix)
260
279
 
261
280
  # eeg数字值转物理值
262
281
  def eeg2phy(self, digital:int):
@@ -444,12 +463,26 @@ class QLBaseDevice(IDevice):
444
463
 
445
464
  return body
446
465
 
466
+ def disconnect(self):
467
+ logger.info(f"[断开设备-{self.device_no}]的连接...")
468
+ self._listening = False
469
+ try:
470
+ sleep(0.1)
471
+ self.socket.shutdown(2)
472
+ self.socket.close()
473
+ logger.info(f"[设备-{self.device_no}]设备连接已断开")
474
+ except Exception as e:
475
+ logger.error(f"断开设备连接异常: {str(e)}")
476
+
477
+ # 关闭解析器
478
+ self._parser.stop()
479
+
447
480
  def __str__(self):
448
481
  return f'''
449
482
  Device:
450
483
  Name: {self.device_name},
451
484
  Type: {hex(self.device_type) if self.device_type else None},
452
- ID: {self.device_id if self.device_id else None},
485
+ ID: {self.device_no if self.device_no else None},
453
486
  Software: {self.software_version},
454
487
  Hardware: {self.hardware_version},
455
488
  Connect Time: {self.connect_time},
@@ -459,8 +492,8 @@ class QLBaseDevice(IDevice):
459
492
  Battery Total: {str(self.battery_total) + "%" if self.battery_total else None}
460
493
  '''
461
494
 
462
- def __eq__(self, other):
463
- return self.device_name == other.device_name and self.device_type == other.device_type and self.device_id == other.device_id
495
+ def __eq__(self, other:IDevice):
496
+ return self.device_type == other.device_type and self.device_no == other.device_no
464
497
 
465
498
  def __hash__(self):
466
- return hash((self.device_name, self.device_type, self.device_id))
499
+ return hash((self.device_type, self.device_no))
@@ -5,15 +5,15 @@ from time import sleep, time_ns
5
5
  from typing import Any, Dict, Literal
6
6
 
7
7
  from loguru import logger
8
+ from qlsdk.core.entity import C256RSPacket
8
9
  from qlsdk.persist import RscEDFHandler
9
- from qlsdk.rsc.interface import IDevice, IParser
10
+ from qlsdk.rsc.device.device_factory import DeviceFactory
10
11
  from qlsdk.rsc.command import *
11
- from qlsdk.rsc.parser.base import TcpMessageParser
12
12
  from qlsdk.rsc.device.base import QLBaseDevice
13
13
 
14
14
  class C256RS(QLBaseDevice):
15
15
 
16
- device_type = 0x40 # C64RS设备类型标识符
16
+ device_type = 0x51 # C64RS设备类型标识符
17
17
 
18
18
  def __init__(self, socket):
19
19
  super().__init__(socket)
@@ -125,13 +125,13 @@ class C256RS(QLBaseDevice):
125
125
  self._edf_handler = None
126
126
  self.storage_enable = True
127
127
 
128
- self._parser: IParser = TcpMessageParser(self)
129
- self._parser.start()
128
+ # self._parser: IParser = RSCMessageParser(self)
129
+ # self._parser.start()
130
130
 
131
131
  # 启动数据接收线程
132
- self._accept = Thread(target=self.accept)
133
- self._accept.daemon = True
134
- self._accept.start()
132
+ # self._accept = Thread(target=self.accept)
133
+ # self._accept.daemon = True
134
+ # self._accept.start()
135
135
 
136
136
  @property
137
137
  def device_no(self):
@@ -140,21 +140,14 @@ class C256RS(QLBaseDevice):
140
140
  @device_no.setter
141
141
  def device_no(self, value: str):
142
142
  self._device_no = value
143
-
144
- @classmethod
145
- def from_parent(cls, parent:IDevice) -> IDevice:
146
- rlt = cls(parent.socket)
147
- rlt.device_id = parent.device_id
148
- rlt._device_no = parent.device_no
149
- return rlt
150
-
143
+
151
144
 
152
145
  def init_edf_handler(self):
153
146
  self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
154
147
  self._edf_handler.set_device_type(self.device_type)
155
- self._edf_handler.set_device_no(self.device_name)
148
+ self._edf_handler.set_device_no(self.device_no)
156
149
  self._edf_handler.set_storage_path(self._storage_path)
157
- self._edf_handler.set_file_prefix(self._file_prefix)
150
+ self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C256RS')
158
151
 
159
152
  @property
160
153
  def edf_handler(self):
@@ -169,7 +162,7 @@ class C256RS(QLBaseDevice):
169
162
  @property
170
163
  def acq_channels(self):
171
164
  if self._acq_channels is None:
172
- self._acq_channels = [i for i in range(1, 63)]
165
+ self._acq_channels = [i for i in range(1, 256)]
173
166
  return self._acq_channels
174
167
  @property
175
168
  def sample_range(self):
@@ -199,15 +192,15 @@ class C256RS(QLBaseDevice):
199
192
 
200
193
  # 接收socket消息
201
194
  def accept(self):
202
- while True:
203
- # 缓冲去4M
204
- data = self.socket.recv(4096*1024)
205
- if not data:
206
- logger.warning(f"设备{self.device_name}连接结束")
207
- break
208
-
209
- self._parser.append(data)
195
+ pass
196
+ # while True:
197
+ # # 缓冲去4M
198
+ # data = self.socket.recv(4096*1024)
199
+ # if not data:
200
+ # logger.warning(f"设备{self.device_name}连接结束")
201
+ # break
210
202
 
203
+ # self._parser.append(data)
211
204
 
212
205
  # socket发送数据
213
206
  def send(self, data):
@@ -226,9 +219,8 @@ class C256RS(QLBaseDevice):
226
219
  self._sample_rate = sample_rate
227
220
  self._sample_range = sample_range
228
221
 
229
- # 通用配置-TODO
230
- def set_config(self, key:str, val: str):
231
- pass
222
+ def _signal_wrapper(self, body: bytes):
223
+ return C256RSPacket().transfer(body)
232
224
 
233
225
  def start_impedance(self):
234
226
  logger.info("启动阻抗测量")
@@ -297,7 +289,7 @@ class C256RS(QLBaseDevice):
297
289
 
298
290
  # 数据队列
299
291
  if q is None:
300
- q = Queue(maxsize=1000)
292
+ q = Queue(maxsize=10240000) # 10M缓存
301
293
 
302
294
  # 队列名称
303
295
  if topic is None:
@@ -362,4 +354,5 @@ class C256RS(QLBaseDevice):
362
354
  def __hash__(self):
363
355
  return hash((self.device_name, self.device_type, self.device_id))
364
356
 
365
-
357
+
358
+ DeviceFactory.register(C256RS.device_type, C256RS)
@@ -30,5 +30,6 @@ DeviceFactory.register(C64RS.device_type, C64RS)
30
30
  DeviceFactory.register(ARSKindling.device_type, ARSKindling)
31
31
 
32
32
 
33
+
33
34
 
34
35
 
@@ -0,0 +1,64 @@
1
+ from abc import ABC, abstractmethod
2
+ import abc
3
+ from typing import Literal
4
+
5
+ class IDevice(ABC):
6
+
7
+ @property
8
+ @abc.abstractmethod
9
+ def device_type(self) -> int:
10
+ pass
11
+
12
+ def set_device_type(self, value: int):
13
+ raise NotImplementedError("Not Supported")
14
+
15
+ def set_storage_path(self, path: str):
16
+ raise NotImplementedError("Not Supported")
17
+
18
+ def set_file_prefix(self, pre: str):
19
+ raise NotImplementedError("Not Supported")
20
+
21
+ def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
22
+ raise NotImplementedError("Not Supported")
23
+
24
+ @property
25
+ def device_no(self) -> str:
26
+ pass
27
+
28
+ def read_msg(self, size: int) -> bytes:
29
+ raise NotImplementedError("Not Supported")
30
+
31
+ def produce(self, body: bytes, type:Literal['signal', 'impedance']="signal") -> None:
32
+ raise NotImplementedError("Not Supported")
33
+
34
+ def start_listening(self):
35
+ raise NotImplementedError("Not Supported")
36
+
37
+ def stop_listening(self):
38
+ raise NotImplementedError("Not Supported")
39
+
40
+ def set_device_no(self, value: str):
41
+ raise NotImplementedError("Not Supported")
42
+
43
+ def from_parent(cls, parent) :
44
+ raise NotImplementedError("Not Supported")
45
+
46
+ def start_acquisition(self) -> None:
47
+ raise NotImplementedError("Not Supported")
48
+
49
+ def stop_acquisition(self) -> None:
50
+ raise NotImplementedError("Not Supported")
51
+
52
+ def subscribe(self, type="signal") -> None:
53
+ raise NotImplementedError("Not Supported")
54
+ def unsubscribe(self, topic) -> None:
55
+ raise NotImplementedError("Not Supported")
56
+
57
+ def start_stimulation(self, type="signal", duration=0) -> None:
58
+ raise NotImplementedError("Not Supported")
59
+
60
+ def stop_stimulation(self) -> None:
61
+ raise NotImplementedError("Not Supported")
62
+ def disconnect(self) -> None:
63
+ raise NotImplementedError("Not Supported")
64
+