qlsdk2 0.5.1.1__tar.gz → 0.6.0__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.
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/PKG-INFO +6 -1
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/README.md +5 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/setup.py +1 -1
- qlsdk2-0.6.0/src/qlsdk/core/entity/__init__.py +222 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/message/udp.py +9 -1
- qlsdk2-0.6.0/src/qlsdk/entity/message.py +0 -0
- qlsdk2-0.6.0/src/qlsdk/entity/signal.py +0 -0
- qlsdk2-0.6.0/src/qlsdk/interface/__init__.py +10 -0
- qlsdk2-0.6.0/src/qlsdk/interface/analyzer.py +2 -0
- qlsdk2-0.6.0/src/qlsdk/interface/collector.py +10 -0
- qlsdk2-0.6.0/src/qlsdk/interface/device.py +2 -0
- qlsdk2-0.6.0/src/qlsdk/interface/parser.py +13 -0
- qlsdk2-0.6.0/src/qlsdk/interface/stimulator.py +2 -0
- qlsdk2-0.6.0/src/qlsdk/interface/store.py +2 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/persist/ars_edf.py +80 -101
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/persist/rsc_edf.py +22 -12
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/command/__init__.py +30 -16
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/device/__init__.py +2 -1
- qlsdk2-0.6.0/src/qlsdk/rsc/device/arskindling.py +324 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/device/base.py +214 -105
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/device/c16_rs.py +1 -5
- qlsdk2-0.6.0/src/qlsdk/rsc/device/c256_rs.py +44 -0
- qlsdk2-0.6.0/src/qlsdk/rsc/device/c64_rs.py +29 -0
- qlsdk2-0.6.0/src/qlsdk/rsc/device/c64s1.py +29 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/device/device_factory.py +1 -0
- qlsdk2-0.6.0/src/qlsdk/rsc/interface/device.py +85 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/interface/parser.py +6 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/manager/container.py +10 -2
- qlsdk2-0.6.0/src/qlsdk/rsc/manager/search.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/network/discover.py +2 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/paradigm.py +127 -6
- qlsdk2-0.6.0/src/qlsdk/rsc/parser/__init__.py +2 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/parser/base.py +11 -6
- qlsdk2-0.6.0/src/qlsdk/rsc/parser/rsc.py +130 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk2.egg-info/PKG-INFO +6 -1
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk2.egg-info/SOURCES.txt +13 -1
- qlsdk2-0.6.0/test/test_bdf.py +212 -0
- qlsdk2-0.5.1.1/src/qlsdk/core/entity/__init__.py +0 -89
- qlsdk2-0.5.1.1/src/qlsdk/rsc/device/arskindling.py +0 -386
- qlsdk2-0.5.1.1/src/qlsdk/rsc/device/c256_rs.py +0 -365
- qlsdk2-0.5.1.1/src/qlsdk/rsc/device/c64_rs.py +0 -359
- qlsdk2-0.5.1.1/src/qlsdk/rsc/device/c64s1.py +0 -365
- qlsdk2-0.5.1.1/src/qlsdk/rsc/interface/device.py +0 -127
- qlsdk2-0.5.1.1/src/qlsdk/rsc/parser/__init__.py +0 -1
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/setup.cfg +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/ar4/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/ar4m/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/crc/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/crc/crctools.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/device.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/exception.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/filter/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/filter/norch.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/local.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/message/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/message/command.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/message/tcp.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/network/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/network/monitor.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/core/utils.py +0 -0
- /qlsdk2-0.5.1.1/src/qlsdk/rsc/manager/search.py → /qlsdk2-0.6.0/src/qlsdk/entity/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/persist/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/persist/edf.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/persist/stream.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/eegion.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/entity.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/interface/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/interface/command.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/interface/handler.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/manager/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/network/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/parser/base-new.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/rsc/proxy.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/sdk/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/sdk/ar4sdk.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/sdk/hub.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/sdk/libs/libAr4SDK.dll +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/sdk/libs/libwinpthread-1.dll +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/x8/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk/x8m/__init__.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk2.egg-info/dependency_links.txt +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk2.egg-info/requires.txt +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/src/qlsdk2.egg-info/top_level.txt +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/test/test.222.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/test/test.py +0 -0
- {qlsdk2-0.5.1.1 → qlsdk2-0.6.0}/test/test_ar4m.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qlsdk2
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: SDK for quanlan device
|
|
5
5
|
Home-page: https://github.com/hehuajun/qlsdk
|
|
6
6
|
Author: hehuajun
|
|
@@ -17,6 +17,11 @@ Provides-Extra: dev
|
|
|
17
17
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
18
18
|
Requires-Dist: twine>=3.0; extra == "dev"
|
|
19
19
|
|
|
20
|
+
## **v0.6.0 (2025-xx-xx)
|
|
21
|
+
#### 新设备
|
|
22
|
+
- C256RS设备
|
|
23
|
+
|
|
24
|
+
|
|
20
25
|
## **v0.5.1.1** (2025-08-24)
|
|
21
26
|
#### 🐛 Bug Fixed
|
|
22
27
|
- 修复仅选择一个通道时,自动保存功能异常的问题
|
|
@@ -0,0 +1,222 @@
|
|
|
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
|
+
self.eeg_p = None # 物理值
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def transfer(body: bytes) -> 'RscPacket':
|
|
27
|
+
packet = RscPacket()
|
|
28
|
+
packet.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
29
|
+
packet.result = body[8]
|
|
30
|
+
packet.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
31
|
+
logger.trace(f"pkg_id: {packet.pkg_id}")
|
|
32
|
+
packet.channels = to_channels(body[13: 45])
|
|
33
|
+
packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
|
|
34
|
+
packet.sample_rate = int.from_bytes(body[49: 53], 'little')
|
|
35
|
+
packet.sample_num = int.from_bytes(body[53: 57], 'little')
|
|
36
|
+
packet.resolution = int(int(body[57]) / 8)
|
|
37
|
+
packet.filter = body[58]
|
|
38
|
+
packet.data_len = int.from_bytes(body[59: 63], 'little')
|
|
39
|
+
# 步径 相同通道的点间隔
|
|
40
|
+
step = int(len(packet.channels) * packet.resolution + 4)
|
|
41
|
+
b_eeg = body[63:]
|
|
42
|
+
ch_num = len(packet.channels)
|
|
43
|
+
# 字节序列(4Cn{channel_size}){sample_num})
|
|
44
|
+
packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
|
|
45
|
+
|
|
46
|
+
packet.eeg = [
|
|
47
|
+
[
|
|
48
|
+
int.from_bytes(b_eeg[i * packet.resolution + 4 + j * step:i * packet.resolution + 4 + j * step + 3], 'big', signed=True)
|
|
49
|
+
for j in range(packet.sample_num)
|
|
50
|
+
]
|
|
51
|
+
for i in range(ch_num)
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
logger.trace(packet)
|
|
55
|
+
|
|
56
|
+
return packet
|
|
57
|
+
|
|
58
|
+
def copy(self) -> 'RscPacket':
|
|
59
|
+
packet = RscPacket()
|
|
60
|
+
packet.time_stamp = self.time_stamp
|
|
61
|
+
packet.pkg_id = self.pkg_id
|
|
62
|
+
packet.result = self.result
|
|
63
|
+
packet.channels = self.channels
|
|
64
|
+
packet.origin_sample_rate = self.origin_sample_rate
|
|
65
|
+
packet.sample_rate = self.sample_rate
|
|
66
|
+
packet.sample_num = self.sample_num
|
|
67
|
+
packet.resolution = self.resolution
|
|
68
|
+
packet.filter = self.filter
|
|
69
|
+
packet.data_len = self.data_len
|
|
70
|
+
packet.trigger = self.trigger
|
|
71
|
+
packet.eeg = self.eeg
|
|
72
|
+
return packet
|
|
73
|
+
|
|
74
|
+
def __str__(self):
|
|
75
|
+
return f"""[
|
|
76
|
+
"time_stamp": {self.time_stamp}
|
|
77
|
+
"pkg_id": {self.pkg_id}
|
|
78
|
+
"channels": {self.channels}
|
|
79
|
+
"trigger": {self.trigger}
|
|
80
|
+
"eeg": {self.eeg}
|
|
81
|
+
]"""
|
|
82
|
+
|
|
83
|
+
class ImpedancePacket(Packet):
|
|
84
|
+
def __init__(self):
|
|
85
|
+
super().__init__()
|
|
86
|
+
self.impedance = None
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def transfer(body:bytes) -> 'ImpedancePacket':
|
|
90
|
+
packet = ImpedancePacket()
|
|
91
|
+
packet.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
92
|
+
# packet.result = body[8]
|
|
93
|
+
packet.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
94
|
+
packet.channels = to_channels(body[13: 45])
|
|
95
|
+
# packet.sample_rate = int.from_bytes(body[45: 49], 'little')
|
|
96
|
+
# packet.sample_len = int.from_bytes(body[49: 53], 'little')
|
|
97
|
+
# packet.resolution = int(int(body[53]) / 8)
|
|
98
|
+
# packet.filter = int(int(body[54]) / 8)
|
|
99
|
+
# packet.wave_type = int(int(body[55]) / 8)
|
|
100
|
+
# packet.wave_freq = int.from_bytes(body[56: 60], 'little')
|
|
101
|
+
# packet.data_len = int.from_bytes(body[60: 64], 'little')
|
|
102
|
+
b_impedance = body[64:]
|
|
103
|
+
packet.impedance = [int.from_bytes(b_impedance[j * 4 : j * 4 + 4], 'little', signed=False) for j in range(len(packet.channels))]
|
|
104
|
+
|
|
105
|
+
logger.trace(f"impedance: {packet}")
|
|
106
|
+
|
|
107
|
+
return packet
|
|
108
|
+
|
|
109
|
+
def __str__(self):
|
|
110
|
+
return f"""[
|
|
111
|
+
"time_stamp": {self.time_stamp}
|
|
112
|
+
"pkg_id": {self.pkg_id}
|
|
113
|
+
"channels": {self.channels}
|
|
114
|
+
"impedance": {self.impedance}
|
|
115
|
+
]"""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class C256RSPacket(Packet):
|
|
119
|
+
def __init__(self):
|
|
120
|
+
super().__init__()
|
|
121
|
+
self.origin_sample_rate = None
|
|
122
|
+
self.sample_rate = None
|
|
123
|
+
self.sample_num = None
|
|
124
|
+
self.resolution = None
|
|
125
|
+
self.filter = None
|
|
126
|
+
self.data_len = None
|
|
127
|
+
self.trigger = None
|
|
128
|
+
self.eeg = None
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def transfer(body: bytes) -> 'RscPacket':
|
|
132
|
+
packet = RscPacket()
|
|
133
|
+
packet.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
134
|
+
packet.result = body[8]
|
|
135
|
+
packet.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
136
|
+
packet.channels = to_channels(body[13: 45])
|
|
137
|
+
logger.trace(f"pkg_id: {packet.pkg_id}, channels: {packet.channels}")
|
|
138
|
+
|
|
139
|
+
packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
|
|
140
|
+
packet.sample_rate = int.from_bytes(body[49: 53], 'little')
|
|
141
|
+
packet.sample_num = int.from_bytes(body[53: 57], 'little')
|
|
142
|
+
packet.resolution = int(int(body[57]) / 8)
|
|
143
|
+
packet.filter = body[58]
|
|
144
|
+
packet.data_len = int.from_bytes(body[59: 63], 'little')
|
|
145
|
+
|
|
146
|
+
# 数据块
|
|
147
|
+
b_eeg = body[63:]
|
|
148
|
+
# 根据值域分割数组-代表设备的4个模块
|
|
149
|
+
ranges = [(1, 64), (65, 128), (129, 192), (193, 256)]
|
|
150
|
+
sub_channels = [[x for x in packet.channels if low <= x <= high] for low, high in ranges]
|
|
151
|
+
# 只处理选中的模块
|
|
152
|
+
sub_channels = [_ for _ in sub_channels if len(_) > 0]
|
|
153
|
+
# 步径 相同通道的点间隔
|
|
154
|
+
step = int(len(packet.channels) * packet.resolution + 4 * len(sub_channels))
|
|
155
|
+
offset = 0
|
|
156
|
+
|
|
157
|
+
# 分按子模块处理
|
|
158
|
+
for channels in sub_channels:
|
|
159
|
+
logger.trace(f"子数组: {channels} 长度: {len(channels)}")
|
|
160
|
+
channel_size = len(channels)
|
|
161
|
+
|
|
162
|
+
# 模块没有选中通道的,跳过
|
|
163
|
+
if channel_size == 0:
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
# 只保留第一个有效模块的trigger,其他的模块是冗余信息,无实际含义
|
|
167
|
+
if packet.trigger is None:
|
|
168
|
+
packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
|
|
169
|
+
logger.trace(f"trigger: {packet.trigger}")
|
|
170
|
+
trigger_positions = [index for index, value in enumerate(packet.trigger) if value != 0]
|
|
171
|
+
if len(trigger_positions) > 0:
|
|
172
|
+
logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[packet.time_stamp + int(pos * 1000 / packet.sample_rate) for pos in trigger_positions]}")
|
|
173
|
+
|
|
174
|
+
eeg = [
|
|
175
|
+
[
|
|
176
|
+
int.from_bytes(b_eeg[offset + step * j + 4 + k * packet.resolution : offset + step * j + 7 + k * packet.resolution], 'big', signed=True)
|
|
177
|
+
for j in range(packet.sample_num)
|
|
178
|
+
]
|
|
179
|
+
for k in range(channel_size)
|
|
180
|
+
]
|
|
181
|
+
packet.eeg = packet.eeg + eeg if packet.eeg else eeg
|
|
182
|
+
|
|
183
|
+
offset += 4 + channel_size * packet.resolution
|
|
184
|
+
|
|
185
|
+
logger.trace(packet)
|
|
186
|
+
return packet
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class C256ImpedancePacket(Packet):
|
|
190
|
+
def __init__(self):
|
|
191
|
+
super().__init__()
|
|
192
|
+
self.impedance = None
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def transfer(body:bytes) -> 'ImpedancePacket':
|
|
196
|
+
packet = ImpedancePacket()
|
|
197
|
+
packet.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
198
|
+
# packet.result = body[8]
|
|
199
|
+
packet.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
200
|
+
packet.channels = to_channels(body[13: 45])
|
|
201
|
+
# packet.sample_rate = int.from_bytes(body[45: 49], 'little')
|
|
202
|
+
# packet.sample_len = int.from_bytes(body[49: 53], 'little')
|
|
203
|
+
# packet.resolution = int(int(body[53]) / 8)
|
|
204
|
+
# packet.filter = int(int(body[54]) / 8)
|
|
205
|
+
# packet.wave_type = int(int(body[55]) / 8)
|
|
206
|
+
# packet.wave_freq = int.from_bytes(body[56: 60], 'little')
|
|
207
|
+
# packet.data_len = int.from_bytes(body[60: 64], 'little')
|
|
208
|
+
b_impedance = body[64:]
|
|
209
|
+
packet.impedance = [int.from_bytes(b_impedance[j : j + 4], 'little', signed=False) for j in range(len(packet.channels))]
|
|
210
|
+
|
|
211
|
+
logger.trace(f"impedance: {packet}")
|
|
212
|
+
|
|
213
|
+
def __str__(self):
|
|
214
|
+
return f"""
|
|
215
|
+
time_stamp: {self.time_stamp}
|
|
216
|
+
pkg_id: {self.pkg_id}
|
|
217
|
+
result: {self.result}
|
|
218
|
+
channels: {self.channels}
|
|
219
|
+
data len: {self.data_len}
|
|
220
|
+
impedance: {self.impedance}
|
|
221
|
+
"""
|
|
222
|
+
|
|
@@ -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
|
|
@@ -85,67 +85,67 @@ class ARSKindlingEDFHandler(object):
|
|
|
85
85
|
"D" : [7, 8, 5, 6, 3, 4, 1, 2, 9, 10, 11, 12, 13, 14],
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
self._channel_mapping = {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
# self._channel_mapping = {
|
|
89
|
+
# 1: "D7",
|
|
90
|
+
# 2: "D8",
|
|
91
|
+
# 3: "D5",
|
|
92
|
+
# 4: "D6",
|
|
93
|
+
# 5: "D3",
|
|
94
|
+
# 6: "D4",
|
|
95
|
+
# 7: "D1",
|
|
96
|
+
# 8: "D2",
|
|
97
|
+
# 9: "D9",
|
|
98
|
+
# 10: "D10",
|
|
99
|
+
# 11: "D11",
|
|
100
|
+
# 12: "D12",
|
|
101
|
+
# 13: "D13",
|
|
102
|
+
# 14: "D14",
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
104
|
+
# 17: "C7",
|
|
105
|
+
# 18: "C8",
|
|
106
|
+
# 19: "C5",
|
|
107
|
+
# 20: "C6",
|
|
108
|
+
# 21: "C3",
|
|
109
|
+
# 22: "C4",
|
|
110
|
+
# 23: "C1",
|
|
111
|
+
# 24: "C2",
|
|
112
|
+
# 25: "C9",
|
|
113
|
+
# 26: "C10",
|
|
114
|
+
# 27: "C11",
|
|
115
|
+
# 28: "C12",
|
|
116
|
+
# 29: "C13",
|
|
117
|
+
# 30: "C14",
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
# 33: "B7",
|
|
120
|
+
# 34: "B8",
|
|
121
|
+
# 35: "B5",
|
|
122
|
+
# 36: "B6",
|
|
123
|
+
# 37: "B3",
|
|
124
|
+
# 38: "B4",
|
|
125
|
+
# 39: "B1",
|
|
126
|
+
# 40: "B2",
|
|
127
|
+
# 41: "B9",
|
|
128
|
+
# 42: "B10",
|
|
129
|
+
# 43: "B11",
|
|
130
|
+
# 44: "B12",
|
|
131
|
+
# 45: "B13",
|
|
132
|
+
# 46: "B14",
|
|
133
133
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
134
|
+
# 49: "A7",
|
|
135
|
+
# 50: "A8",
|
|
136
|
+
# 51: "A5",
|
|
137
|
+
# 52: "A6",
|
|
138
|
+
# 53: "A3",
|
|
139
|
+
# 54: "A4",
|
|
140
|
+
# 55: "A1",
|
|
141
|
+
# 56: "A2",
|
|
142
|
+
# 57: "A9",
|
|
143
|
+
# 58: "A10",
|
|
144
|
+
# 59: "A11",
|
|
145
|
+
# 60: "A12",
|
|
146
|
+
# 61: "A13",
|
|
147
|
+
# 62: "A14"
|
|
148
|
+
# }
|
|
149
149
|
|
|
150
150
|
self._lock = Lock()
|
|
151
151
|
|
|
@@ -172,6 +172,8 @@ class ARSKindlingEDFHandler(object):
|
|
|
172
172
|
self._device_type = "C64RS"
|
|
173
173
|
elif device_type == 0x40:
|
|
174
174
|
self._device_type = "LJ64S1"
|
|
175
|
+
elif device_type == 0x60:
|
|
176
|
+
self._device_type = "ArsKindling"
|
|
175
177
|
else:
|
|
176
178
|
self._device_type = hex(device_type)
|
|
177
179
|
|
|
@@ -195,55 +197,32 @@ class ARSKindlingEDFHandler(object):
|
|
|
195
197
|
if packet is None:
|
|
196
198
|
# self._edf_writer_thread.stop_recording()
|
|
197
199
|
for k in self._edf_handler.keys():
|
|
198
|
-
self._edf_handler[k].
|
|
200
|
+
self._edf_handler[k].write(None)
|
|
199
201
|
return
|
|
200
202
|
|
|
201
|
-
#按分区写入数据
|
|
202
|
-
|
|
203
|
+
#按分区写入数据
|
|
203
204
|
for k in self._channel_spilt.keys():
|
|
204
|
-
logger.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
# with self._lock:
|
|
209
|
-
# if self.channels is None:
|
|
210
|
-
# logger.info(f"开始记录数据到文件...")
|
|
211
|
-
# self.channels = packet.channels
|
|
212
|
-
# self._first_pkg_id = packet.pkg_id if self._first_pkg_id is None else self._first_pkg_id
|
|
213
|
-
# self._first_timestamp = packet.time_stamp if self._first_timestamp is None else self._first_timestamp
|
|
214
|
-
# self._start_time = datetime.now()
|
|
215
|
-
# logger.info(f"第一个包id: {self._first_pkg_id }, 时间戳:{self._first_timestamp}, 当前时间:{datetime.now().timestamp()} offset: {datetime.now().timestamp() - self._first_timestamp}")
|
|
216
|
-
|
|
217
|
-
# if self._last_pkg_id and self._last_pkg_id != packet.pkg_id - 1:
|
|
218
|
-
# self._lost_packets += packet.pkg_id - self._last_pkg_id - 1
|
|
219
|
-
# logger.warning(f"数据包丢失: {self._last_pkg_id} -> {packet.pkg_id}, 丢包数: {packet.pkg_id - self._last_pkg_id - 1}")
|
|
220
|
-
|
|
221
|
-
# self._last_pkg_id = packet.pkg_id
|
|
222
|
-
# self._total_packets += 1
|
|
223
|
-
|
|
224
|
-
# if self._edf_writer_thread is None:
|
|
225
|
-
# self._edf_writer_thread = EDFStreamWriter(self.channels, self.sample_rate, self.physical_max, self.physical_min, self.file_type, self.file_name)
|
|
226
|
-
# self._edf_writer_thread.set_start_time(self._start_time)
|
|
227
|
-
# self._edf_writer_thread.start()
|
|
228
|
-
# logger.info(f"开始写入数据: {self.file_name}")
|
|
229
|
-
|
|
230
|
-
# self._edf_writer_thread.append(packet.eeg)
|
|
205
|
+
logger.trace(f'分区{k}写入数据到文件')
|
|
206
|
+
if k in packet.channels.keys():
|
|
207
|
+
p = packet
|
|
208
|
+
self.writeA(p, k)
|
|
231
209
|
|
|
232
|
-
def writeA(self, packet: RscPacket,
|
|
210
|
+
def writeA(self, packet: RscPacket, name='A'):
|
|
233
211
|
# 参数检查
|
|
234
|
-
if packet is None
|
|
212
|
+
if packet is None:
|
|
235
213
|
logger.warning("空数据,忽略")
|
|
236
214
|
return
|
|
215
|
+
|
|
216
|
+
channels = packet.channels.get(name, None)
|
|
217
|
+
eeg = packet.eeg.get(name, None)
|
|
237
218
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
logger.debug(f"没有指定分区{name}的通道,跳过")
|
|
242
|
-
pass
|
|
219
|
+
if eeg is None :
|
|
220
|
+
logger.trace(f"分区{name}没有可用的数据,跳过文件写入")
|
|
221
|
+
return
|
|
243
222
|
|
|
244
223
|
# 分区数据包写入
|
|
245
224
|
if name not in self._edf_handler.keys():
|
|
246
|
-
edf_handler = RscEDFHandler(self.sample_rate, self.
|
|
225
|
+
edf_handler = RscEDFHandler(self.sample_rate, self.physical_max , self.physical_min, self.resolution)
|
|
247
226
|
edf_handler.set_device_type(self._device_type)
|
|
248
227
|
edf_handler.set_device_no(self._device_no)
|
|
249
228
|
edf_handler.set_storage_path(self._storage_path)
|
|
@@ -251,15 +230,12 @@ class ARSKindlingEDFHandler(object):
|
|
|
251
230
|
logger.info(f"开始写入分区{name}的数据到文件")
|
|
252
231
|
self._edf_handler[name] = edf_handler
|
|
253
232
|
|
|
254
|
-
# 保留本分区的通道和数据
|
|
255
|
-
channels = [packet.channels[p] for p in channel_pos]
|
|
256
|
-
eeg = [packet.eeg[p] for p in channel_pos]
|
|
257
|
-
|
|
258
233
|
# 新建数据包实例
|
|
259
234
|
data = RscPacket()
|
|
260
235
|
data.time_stamp = packet.time_stamp
|
|
261
236
|
data.pkg_id = packet.pkg_id
|
|
262
|
-
data.channels = channels
|
|
237
|
+
# data.channels = channels
|
|
238
|
+
data.channels = self.channel_display(channels, name)
|
|
263
239
|
data.origin_sample_rate = packet.origin_sample_rate
|
|
264
240
|
data.sample_rate = packet.sample_rate
|
|
265
241
|
data.sample_num = packet.sample_num
|
|
@@ -276,3 +252,6 @@ class ARSKindlingEDFHandler(object):
|
|
|
276
252
|
# trigger现在(20250702)多个分区共享, 分发到所有分区文件中标记
|
|
277
253
|
for k in self._edf_handler.keys():
|
|
278
254
|
self._edf_handler[k].trigger(desc, cur_time)
|
|
255
|
+
|
|
256
|
+
def channel_display(self, channel, name: str):
|
|
257
|
+
return [f'{name}-{channel_id}' for channel_id in channel]
|