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 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.info(f"dig data eeg: {packet.eeg}")
43
- logger.info(f"dig data acc: {packet.acc}")
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.info(f"phy data eeg: {packet.eeg}")
47
- logger.info(f"phy data acc: {packet.acc}")
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.device_type = None
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
@@ -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
- def parse_body(self, body: bytes):
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.warning(f"通道 {channels} 刺激开始")
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
- logger.info(f"Received stimulation stop response: {body.hex()}")
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
- characteristic = int.from_bytes(body[25:29], 'little')
259
- logger.warning(f"[{characteristic}]刺激告警,通道 {channels} 刺激驱动不足")
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
- if len(self.device.signal_consumers) > 0 or self.device.edf_handler:
281
- # 解析数据包
282
- rsc = RscPacket()
283
- rsc.transfer(body)
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(rsc)
311
+ q.put(packet)
288
312
 
289
- # 文件写入到edf
290
- if self.device.edf_handler:
291
- self.device.edf_handler.append(rsc)
313
+
292
314
 
293
315
  # =============================================================================
294
- # Command Registration
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
- def trigger(self, data):
94
- pass
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