qlsdk2 0.4.0a3__py3-none-any.whl → 0.4.1__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/ar4/__init__.py +9 -5
- qlsdk/core/device.py +10 -1
- qlsdk/core/message/command.py +56 -35
- qlsdk/persist/edf.py +21 -2
- qlsdk/persist/rsc_edf.py +187 -128
- qlsdk/rsc/__init__.py +4 -2
- qlsdk/rsc/command/__init__.py +183 -61
- qlsdk/rsc/command/message.py +171 -74
- qlsdk/rsc/device/__init__.py +2 -0
- qlsdk/rsc/device/base.py +388 -0
- qlsdk/rsc/device/c64_rs.py +364 -0
- qlsdk/rsc/device/device_factory.py +29 -0
- qlsdk/rsc/entity.py +63 -24
- qlsdk/rsc/interface/__init__.py +3 -0
- qlsdk/rsc/interface/command.py +10 -0
- qlsdk/rsc/interface/device.py +107 -0
- qlsdk/rsc/interface/handler.py +9 -0
- qlsdk/rsc/interface/parser.py +9 -0
- qlsdk/rsc/manager/__init__.py +2 -0
- qlsdk/rsc/manager/container.py +121 -0
- qlsdk/rsc/manager/search.py +0 -0
- qlsdk/rsc/network/__init__.py +1 -0
- qlsdk/rsc/network/discover.py +87 -0
- qlsdk/rsc/paradigm.py +1 -1
- qlsdk/rsc/parser/__init__.py +1 -0
- qlsdk/rsc/parser/base.py +66 -0
- qlsdk/sdk/ar4sdk.py +13 -4
- qlsdk/x8/__init__.py +4 -0
- {qlsdk2-0.4.0a3.dist-info → qlsdk2-0.4.1.dist-info}/METADATA +1 -1
- qlsdk2-0.4.1.dist-info/RECORD +58 -0
- qlsdk2-0.4.0a3.dist-info/RECORD +0 -42
- {qlsdk2-0.4.0a3.dist-info → qlsdk2-0.4.1.dist-info}/WHEEL +0 -0
- {qlsdk2-0.4.0a3.dist-info → qlsdk2-0.4.1.dist-info}/top_level.txt +0 -0
qlsdk/ar4/__init__.py
CHANGED
|
@@ -39,12 +39,12 @@ class AR4(LMDevice):
|
|
|
39
39
|
consumer.put(packet)
|
|
40
40
|
|
|
41
41
|
if len(self._phy_subscriber) > 0:
|
|
42
|
-
logger.
|
|
43
|
-
logger.
|
|
42
|
+
logger.debug(f"dig data eeg: {packet.eeg}")
|
|
43
|
+
logger.debug(f"dig data acc: {packet.acc}")
|
|
44
44
|
packet.eeg = self.eeg2phy(np.array(packet.eeg))
|
|
45
45
|
packet.acc = self.acc2phy(np.array(packet.acc))
|
|
46
|
-
logger.
|
|
47
|
-
logger.
|
|
46
|
+
logger.debug(f"phy data eeg: {packet.eeg}")
|
|
47
|
+
logger.debug(f"phy data acc: {packet.acc}")
|
|
48
48
|
for consumer2 in self._phy_subscriber.values():
|
|
49
49
|
consumer2.put(packet)
|
|
50
50
|
|
|
@@ -74,7 +74,11 @@ class AR4(LMDevice):
|
|
|
74
74
|
self._edf_handler = None
|
|
75
75
|
self._recording = False
|
|
76
76
|
logger.info(f"停止记录数据: {self.box_mac}")
|
|
77
|
-
|
|
77
|
+
|
|
78
|
+
def trigger(self, desc):
|
|
79
|
+
if self._edf_handler:
|
|
80
|
+
self._edf_handler.trigger(desc)
|
|
81
|
+
|
|
78
82
|
# 订阅推送消息
|
|
79
83
|
def subscribe(self, topic: str = None, q: Queue = Queue(), value_type: Literal['phy', 'dig'] = 'phy') -> tuple[str, Queue]:
|
|
80
84
|
if topic is None:
|
qlsdk/core/device.py
CHANGED
|
@@ -4,9 +4,18 @@ class BaseDevice(object):
|
|
|
4
4
|
def __init__(self, socket = None):
|
|
5
5
|
self.socket = socket
|
|
6
6
|
self.device_name = None
|
|
7
|
-
self.
|
|
7
|
+
self._device_type = None
|
|
8
8
|
self.device_id = None
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def device_type(self) -> int:
|
|
13
|
+
return self._device_type
|
|
14
|
+
|
|
15
|
+
@device_type.setter
|
|
16
|
+
def device_type(self, value: int):
|
|
17
|
+
self._device_type = value
|
|
18
|
+
|
|
10
19
|
@property
|
|
11
20
|
def acq_channels(self) :
|
|
12
21
|
return None
|
qlsdk/core/message/command.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import numpy as np
|
|
2
3
|
from time import time_ns
|
|
3
4
|
from typing import Dict, Type
|
|
4
|
-
from enum import Enum
|
|
5
5
|
from loguru import logger
|
|
6
|
+
|
|
7
|
+
|
|
6
8
|
from qlsdk.core.crc import crc16
|
|
7
9
|
from qlsdk.core.device import BaseDevice
|
|
8
10
|
from qlsdk.core.entity import RscPacket, ImpedancePacket
|
|
@@ -64,6 +66,13 @@ class DeviceCommand(abc.ABC):
|
|
|
64
66
|
# 解析消息体
|
|
65
67
|
body = payload[self.HEADER_LEN:-2]
|
|
66
68
|
|
|
69
|
+
def parse_body(self, body: bytes):
|
|
70
|
+
time = int.from_bytes(body[0:8], 'little')
|
|
71
|
+
# result - 1B
|
|
72
|
+
result = body[8]
|
|
73
|
+
logger.info(f"[{time}]{self.cmd_desc}{'成功' if result == 0 else '失败'}")
|
|
74
|
+
|
|
75
|
+
|
|
67
76
|
|
|
68
77
|
class CommandFactory:
|
|
69
78
|
"""Registry for command implementations"""
|
|
@@ -114,6 +123,8 @@ class GetDeviceInfoCommand(DeviceCommand):
|
|
|
114
123
|
flag = int.from_bytes(body[41:45], 'little')
|
|
115
124
|
logger.debug(f"Received device info: {result}, {flag}, {self.device}")
|
|
116
125
|
|
|
126
|
+
# 创建设备对象
|
|
127
|
+
|
|
117
128
|
|
|
118
129
|
# 握手
|
|
119
130
|
class HandshakeCommand(DeviceCommand):
|
|
@@ -160,8 +171,6 @@ class SetAcquisitionParamCommand(DeviceCommand):
|
|
|
160
171
|
body += bytes.fromhex('00')
|
|
161
172
|
|
|
162
173
|
return body
|
|
163
|
-
def parse_body(self, body: bytes):
|
|
164
|
-
logger.info(f"Received SetAcquisitionParam response: {body.hex()}")
|
|
165
174
|
|
|
166
175
|
# 启动采集
|
|
167
176
|
class StartAcquisitionCommand(DeviceCommand):
|
|
@@ -170,8 +179,6 @@ class StartAcquisitionCommand(DeviceCommand):
|
|
|
170
179
|
|
|
171
180
|
def pack_body(self):
|
|
172
181
|
return bytes.fromhex('0000')
|
|
173
|
-
def parse_body(self, body: bytes):
|
|
174
|
-
logger.info(f"Received acquisition start response: {body.hex()}")
|
|
175
182
|
|
|
176
183
|
# 停止采集
|
|
177
184
|
class StopAcquisitionCommand(DeviceCommand):
|
|
@@ -180,17 +187,14 @@ class StopAcquisitionCommand(DeviceCommand):
|
|
|
180
187
|
|
|
181
188
|
def pack_body(self):
|
|
182
189
|
return b''
|
|
183
|
-
|
|
184
|
-
logger.info(f"Received acquisition stop response: {body.hex()}")
|
|
190
|
+
|
|
185
191
|
|
|
186
192
|
# 设置阻抗采集参数
|
|
187
193
|
class SetImpedanceParamCommand(DeviceCommand):
|
|
188
194
|
cmd_code = 0x411
|
|
189
195
|
cmd_desc = "设置阻抗测量参数"
|
|
190
|
-
def parse_body(self, body: bytes):
|
|
191
|
-
logger.info(f"Received SetImpedanceParamCommand response: {body.hex()}")
|
|
192
196
|
|
|
193
|
-
#
|
|
197
|
+
# 启动阻抗测量
|
|
194
198
|
class StartImpedanceCommand(DeviceCommand):
|
|
195
199
|
cmd_code = 0x412
|
|
196
200
|
cmd_desc = "启动阻抗测量"
|
|
@@ -200,19 +204,14 @@ class StartImpedanceCommand(DeviceCommand):
|
|
|
200
204
|
body += bytes.fromhex('0000000000000000') # 8字节占位符
|
|
201
205
|
return body
|
|
202
206
|
|
|
203
|
-
def parse_body(self, body: bytes):
|
|
204
|
-
logger.info(f"Received StartImpedanceCommand response: {body.hex()}")
|
|
205
207
|
|
|
206
|
-
#
|
|
208
|
+
# 停止阻抗测量
|
|
207
209
|
class StopImpedanceCommand(DeviceCommand):
|
|
208
210
|
cmd_code = 0x413
|
|
209
211
|
cmd_desc = "停止阻抗测量"
|
|
210
212
|
|
|
211
213
|
def pack_body(self):
|
|
212
214
|
return b''
|
|
213
|
-
|
|
214
|
-
def parse_body(self, body: bytes):
|
|
215
|
-
logger.info(f"Received StopImpedanceCommand response: {body.hex()}")
|
|
216
215
|
|
|
217
216
|
# 启动刺激
|
|
218
217
|
class StartStimulationCommand(DeviceCommand):
|
|
@@ -229,19 +228,36 @@ class StartStimulationCommand(DeviceCommand):
|
|
|
229
228
|
# error_channel - 8B
|
|
230
229
|
# error_channel= int.from_bytes(body[9:17], 'big')
|
|
231
230
|
channels = to_channels(body[9:17])
|
|
232
|
-
logger.
|
|
231
|
+
logger.success(f"通道 {channels} 刺激开始")
|
|
232
|
+
self.device.trigger(f"通道 {channels} 刺激开始")
|
|
233
233
|
# error_type - 1B
|
|
234
234
|
error_type = body[17]
|
|
235
235
|
|
|
236
|
-
#
|
|
236
|
+
# 停止刺激
|
|
237
237
|
class StopStimulationCommand(DeviceCommand):
|
|
238
238
|
cmd_code = 0x488
|
|
239
239
|
cmd_desc = "停止刺激"
|
|
240
|
-
|
|
240
|
+
|
|
241
|
+
# 启动刺激
|
|
242
|
+
class StopStimulationNotifyCommand(DeviceCommand):
|
|
243
|
+
cmd_code = 0x48D
|
|
244
|
+
cmd_desc = "停止刺激通知"
|
|
245
|
+
def pack_body(self):
|
|
246
|
+
return self.device.stim_paradigm.to_bytes()
|
|
247
|
+
# return bytes.fromhex('01000000000000008813000000000000010000000000000000000140420f00640064000000803f0000010000000000000000000000000000000000000000008813000000000000')
|
|
241
248
|
def parse_body(self, body: bytes):
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
#
|
|
249
|
+
# time - 8B
|
|
250
|
+
time = int.from_bytes(body[0:8], 'little')
|
|
251
|
+
# result - 1B
|
|
252
|
+
result = body[8]
|
|
253
|
+
# error_channel - 8B
|
|
254
|
+
# error_channel= int.from_bytes(body[9:17], 'big')
|
|
255
|
+
channels = to_channels(body[9:17])
|
|
256
|
+
logger.success(f"通道 {channels} 刺激结束")
|
|
257
|
+
self.device.trigger(f"通道 {channels} 刺激结束", time)
|
|
258
|
+
# error_type - 1B
|
|
259
|
+
error_type = body[17]
|
|
260
|
+
# 刺激信息
|
|
245
261
|
class StimulationInfoCommand(DeviceCommand):
|
|
246
262
|
cmd_code = 0x48e
|
|
247
263
|
cmd_desc = "刺激告警信息"
|
|
@@ -254,9 +270,10 @@ class StimulationInfoCommand(DeviceCommand):
|
|
|
254
270
|
channels = to_channels(body[9:17])
|
|
255
271
|
# 保留位-8B
|
|
256
272
|
# error_type - 1B
|
|
273
|
+
err_type = body[17]
|
|
257
274
|
# 特征位-4B
|
|
258
|
-
|
|
259
|
-
logger.warning(f"[{
|
|
275
|
+
# errType = int.from_bytes(body[25:29], 'little')
|
|
276
|
+
logger.warning(f"刺激告警信息[{err_type}],通道 {channels} 刺激驱动不足")
|
|
260
277
|
|
|
261
278
|
|
|
262
279
|
# 阻抗数据
|
|
@@ -276,24 +293,28 @@ class SignalDataCommand(DeviceCommand):
|
|
|
276
293
|
def unpack(self, payload):
|
|
277
294
|
return super().unpack(payload)
|
|
278
295
|
|
|
279
|
-
def parse_body(self, body: bytes):
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
296
|
+
def parse_body(self, body: bytes):
|
|
297
|
+
# 解析数据包
|
|
298
|
+
packet = RscPacket()
|
|
299
|
+
packet.transfer(body)
|
|
300
|
+
|
|
301
|
+
# 文件写入到edf
|
|
302
|
+
if self.device.edf_handler:
|
|
303
|
+
self.device.edf_handler.write(packet)
|
|
304
|
+
|
|
305
|
+
if len(self.device.signal_consumers) > 0 :
|
|
306
|
+
# 信号数字值转物理值
|
|
307
|
+
packet.eeg = self.device.eeg2phy(np.array(packet.eeg))
|
|
284
308
|
|
|
285
309
|
# 发送数据包到订阅者
|
|
286
310
|
for q in list(self.device.signal_consumers.values()):
|
|
287
|
-
q.put(
|
|
311
|
+
q.put(packet)
|
|
288
312
|
|
|
289
|
-
|
|
290
|
-
if self.device.edf_handler:
|
|
291
|
-
self.device.edf_handler.append(rsc)
|
|
313
|
+
|
|
292
314
|
|
|
293
315
|
# =============================================================================
|
|
294
|
-
#
|
|
316
|
+
# 指令实现类注册到指令工厂
|
|
295
317
|
# =============================================================================
|
|
296
|
-
|
|
297
318
|
CommandFactory.register_command(DefaultCommand.cmd_code, DefaultCommand)
|
|
298
319
|
CommandFactory.register_command(GetDeviceInfoCommand.cmd_code, GetDeviceInfoCommand)
|
|
299
320
|
CommandFactory.register_command(HandshakeCommand.cmd_code, HandshakeCommand)
|
qlsdk/persist/edf.py
CHANGED
|
@@ -34,6 +34,7 @@ class EdfHandler(object):
|
|
|
34
34
|
self._first_pkg_id = None
|
|
35
35
|
self._last_pkg_id = None
|
|
36
36
|
self._first_timestamp = None
|
|
37
|
+
self._first_time = None
|
|
37
38
|
self._end_time = None
|
|
38
39
|
self._patient_code = "patient_code"
|
|
39
40
|
self._patient_name = "patient_name"
|
|
@@ -77,6 +78,7 @@ class EdfHandler(object):
|
|
|
77
78
|
self.acc_channels = data.acc_ch_count
|
|
78
79
|
self._first_pkg_id = data.pkg_id
|
|
79
80
|
self._first_timestamp = data.time_stamp
|
|
81
|
+
self._first_time = datetime.now()
|
|
80
82
|
|
|
81
83
|
if self._last_pkg_id and self._last_pkg_id != data.pkg_id - 1:
|
|
82
84
|
self._lost_packets += data.pkg_id - self._last_pkg_id - 1
|
|
@@ -90,8 +92,25 @@ class EdfHandler(object):
|
|
|
90
92
|
if not self._recording:
|
|
91
93
|
self.start()
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
# trigger标记
|
|
96
|
+
# desc: 标记内容
|
|
97
|
+
# cur_time: 设备时间时间戳,非设备发出的trigger不要设置
|
|
98
|
+
def trigger(self, desc, cur_time=None):
|
|
99
|
+
if self._edf_writer is None:
|
|
100
|
+
logger.warning("EDF writer未初始化,无法写入trigger标记")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
if cur_time is None:
|
|
104
|
+
onset = datetime.now().timestamp() - self._first_time.timestamp()
|
|
105
|
+
else:
|
|
106
|
+
onset = cur_time - self._first_timestamp
|
|
107
|
+
|
|
108
|
+
self._edf_writer.writeAnnotation(onset, 1, desc)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# def trigger(self, desc):
|
|
112
|
+
# if self._edf_writer:
|
|
113
|
+
# self._edf_writer.writeAnnotation(0, 1, desc)
|
|
95
114
|
|
|
96
115
|
def start(self):
|
|
97
116
|
self._recording = True
|