qlsdk2 0.5.1__py3-none-any.whl → 0.6.0__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/core/entity/__init__.py +175 -42
- qlsdk/core/message/udp.py +9 -1
- qlsdk/entity/__init__.py +0 -0
- qlsdk/entity/message.py +0 -0
- qlsdk/entity/signal.py +0 -0
- qlsdk/interface/__init__.py +10 -0
- qlsdk/interface/analyzer.py +2 -0
- qlsdk/interface/collector.py +10 -0
- qlsdk/interface/device.py +2 -0
- qlsdk/interface/parser.py +13 -0
- qlsdk/interface/stimulator.py +2 -0
- qlsdk/interface/store.py +2 -0
- qlsdk/persist/ars_edf.py +80 -101
- qlsdk/persist/rsc_edf.py +23 -13
- qlsdk/rsc/command/__init__.py +30 -16
- qlsdk/rsc/device/__init__.py +2 -1
- qlsdk/rsc/device/arskindling.py +248 -310
- qlsdk/rsc/device/base.py +214 -105
- qlsdk/rsc/device/c16_rs.py +1 -5
- qlsdk/rsc/device/c256_rs.py +17 -338
- qlsdk/rsc/device/c64_rs.py +2 -332
- qlsdk/rsc/device/c64s1.py +4 -340
- qlsdk/rsc/device/device_factory.py +1 -0
- qlsdk/rsc/interface/device.py +38 -80
- qlsdk/rsc/interface/parser.py +6 -0
- qlsdk/rsc/manager/container.py +10 -2
- qlsdk/rsc/network/discover.py +2 -0
- qlsdk/rsc/paradigm.py +127 -6
- qlsdk/rsc/parser/__init__.py +2 -1
- qlsdk/rsc/parser/base.py +11 -6
- qlsdk/rsc/parser/rsc.py +130 -0
- {qlsdk2-0.5.1.dist-info → qlsdk2-0.6.0.dist-info}/METADATA +25 -15
- {qlsdk2-0.5.1.dist-info → qlsdk2-0.6.0.dist-info}/RECORD +35 -24
- {qlsdk2-0.5.1.dist-info → qlsdk2-0.6.0.dist-info}/WHEEL +0 -0
- {qlsdk2-0.5.1.dist-info → qlsdk2-0.6.0.dist-info}/top_level.txt +0 -0
qlsdk/persist/rsc_edf.py
CHANGED
|
@@ -15,7 +15,7 @@ EDF_FILE_TYPE = {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
class EDFStreamWriter(Thread):
|
|
18
|
-
def __init__(self, channels, sample_frequency, physical_max,
|
|
18
|
+
def __init__(self, channels, sample_frequency, physical_max, physical_min, file_type, file_path, record_duration=None):
|
|
19
19
|
|
|
20
20
|
super().__init__()
|
|
21
21
|
self._writer : EdfWriter = None
|
|
@@ -32,7 +32,7 @@ class EDFStreamWriter(Thread):
|
|
|
32
32
|
self._n_channels = len(channels)
|
|
33
33
|
self.sample_frequency = sample_frequency
|
|
34
34
|
self.physical_max = physical_max
|
|
35
|
-
self.physical_min =
|
|
35
|
+
self.physical_min = physical_min
|
|
36
36
|
# 和位数相关,edf 16 bits/bdf 24 bits
|
|
37
37
|
self.digital_max = 8388607 if file_type == EDF_FILE_TYPE['bdf'] else 32767
|
|
38
38
|
self.digital_min = -8388608 if file_type == EDF_FILE_TYPE['bdf'] else -32768
|
|
@@ -46,6 +46,7 @@ class EDFStreamWriter(Thread):
|
|
|
46
46
|
self.equipment = "equipment"
|
|
47
47
|
self.patient_code = "patient_code"
|
|
48
48
|
self.patient_name = "patient_name"
|
|
49
|
+
logger.info(f'digital_max:{self.digital_max}, digital_min:{self.digital_min}, physical_max:{self.physical_max}, physical_min:{self.physical_min}')
|
|
49
50
|
|
|
50
51
|
def set_channels(self, channels):
|
|
51
52
|
self._channels = channels
|
|
@@ -61,7 +62,7 @@ class EDFStreamWriter(Thread):
|
|
|
61
62
|
self._recording = False
|
|
62
63
|
|
|
63
64
|
def append(self, data):
|
|
64
|
-
if data:
|
|
65
|
+
if data is not None:
|
|
65
66
|
# 数据
|
|
66
67
|
self.data_queue.put(data)
|
|
67
68
|
|
|
@@ -91,7 +92,7 @@ class EDFStreamWriter(Thread):
|
|
|
91
92
|
logger.debug("收到结束信号,停止写入数据")
|
|
92
93
|
break
|
|
93
94
|
# 处理数据
|
|
94
|
-
self._points += len(data[
|
|
95
|
+
self._points += len(data[0])
|
|
95
96
|
logger.trace(f"已处理数据点数:{self._points}")
|
|
96
97
|
self._write_file(data)
|
|
97
98
|
# 有数据重置计数器
|
|
@@ -127,6 +128,7 @@ class EDFStreamWriter(Thread):
|
|
|
127
128
|
|
|
128
129
|
# 配置通道参数
|
|
129
130
|
signal_headers = []
|
|
131
|
+
logger.trace(f"sf: {self.sample_frequency}, pm: {self.physical_max}, pn: {self.physical_min}, dm: {self.digital_max}, dn: {self.digital_min}")
|
|
130
132
|
for ch in range(self._n_channels):
|
|
131
133
|
signal_headers.append({
|
|
132
134
|
"label": f'channels {self._channels[ch]}',
|
|
@@ -169,14 +171,16 @@ class EDFStreamWriter(Thread):
|
|
|
169
171
|
# 写入1秒的数据
|
|
170
172
|
def _write_block(self, block):
|
|
171
173
|
logger.trace(f"写入数据: {block}")
|
|
172
|
-
# 转换数据类型为float64
|
|
173
|
-
|
|
174
|
+
# 转换数据类型为float64-物理信号uV int32-数字信号
|
|
175
|
+
data_input = block.astype(np.int32)
|
|
176
|
+
logger.trace(f"写入数据-real: {data_input}")
|
|
174
177
|
# 写入时转置为(样本数, 通道数)格式
|
|
175
|
-
self._writer.writeSamples(
|
|
178
|
+
self._writer.writeSamples(data_input, digital=True)
|
|
176
179
|
self._duration += 1
|
|
177
180
|
|
|
178
181
|
if self._duration % 10 == 0: # 每10秒打印一次进度
|
|
179
182
|
logger.info(f"数据记录中... 文件名:{self.file_path}, 已记录时长: {self._duration}秒")
|
|
183
|
+
|
|
180
184
|
|
|
181
185
|
# 用作数据结构一致化处理,通过调用公共类写入edf文件
|
|
182
186
|
# 入参包含写入edf的全部前置参数
|
|
@@ -200,7 +204,7 @@ class RscEDFHandler(object):
|
|
|
200
204
|
self.physical_max = physical_max
|
|
201
205
|
self.physical_min = physical_min
|
|
202
206
|
self.digital_max = 8388607 if resolution == 24 else 32767
|
|
203
|
-
self.digital_min = -
|
|
207
|
+
self.digital_min = -8388608 if resolution == 24 else - 32768
|
|
204
208
|
self.file_type = EDF_FILE_TYPE["bdf"] if resolution == 24 else EDF_FILE_TYPE["edf"]
|
|
205
209
|
# 点分辨率
|
|
206
210
|
self.resolution = resolution
|
|
@@ -226,8 +230,8 @@ class RscEDFHandler(object):
|
|
|
226
230
|
self._end_time = None
|
|
227
231
|
self._patient_code = "patient_code"
|
|
228
232
|
self._patient_name = "patient_name"
|
|
229
|
-
self._device_type = "
|
|
230
|
-
self._device_no = "
|
|
233
|
+
self._device_type = "0000"
|
|
234
|
+
self._device_no = "00000000"
|
|
231
235
|
self._total_packets = 0
|
|
232
236
|
self._lost_packets = 0
|
|
233
237
|
self._storage_path = storage_path
|
|
@@ -242,7 +246,7 @@ class RscEDFHandler(object):
|
|
|
242
246
|
suffix = "bdf" if self.resolution == 24 else "edf"
|
|
243
247
|
|
|
244
248
|
# 文件名称
|
|
245
|
-
file_name = f"{self._file_prefix}_{self._device_no}_{self._start_time.strftime('%y%m%d%H%
|
|
249
|
+
file_name = f"{self._file_prefix}_{self._device_no}_{self._start_time.strftime('%y%m%d%H%M%S')}.{suffix}" if self._file_prefix else f"{self._device_no}_{self._start_time.strftime('%y%m%d%H%I%M')}.{suffix}"
|
|
246
250
|
|
|
247
251
|
if self._storage_path:
|
|
248
252
|
try:
|
|
@@ -260,6 +264,10 @@ class RscEDFHandler(object):
|
|
|
260
264
|
self._device_type = "C64RS"
|
|
261
265
|
elif device_type == 0x40:
|
|
262
266
|
self._device_type = "LJ64S1"
|
|
267
|
+
elif device_type == 0x45:
|
|
268
|
+
self._device_type = "C256RS"
|
|
269
|
+
elif device_type == 0x51:
|
|
270
|
+
self._device_type = "C256RS"
|
|
263
271
|
elif device_type == 0x60:
|
|
264
272
|
self._device_type = "ARSKindling"
|
|
265
273
|
elif device_type == 0x339:
|
|
@@ -288,6 +296,8 @@ class RscEDFHandler(object):
|
|
|
288
296
|
logger.info(f"收到结束信号,即将停止写入数据:{self.file_name}")
|
|
289
297
|
self._edf_writer_thread.stop_recording()
|
|
290
298
|
return
|
|
299
|
+
|
|
300
|
+
logger.debug(f"packet: {packet}")
|
|
291
301
|
|
|
292
302
|
with self._lock:
|
|
293
303
|
if self.channels is None:
|
|
@@ -310,6 +320,7 @@ class RscEDFHandler(object):
|
|
|
310
320
|
self._edf_writer_thread.set_start_time(self._start_time)
|
|
311
321
|
self._edf_writer_thread.start()
|
|
312
322
|
logger.info(f"开始写入数据: {self.file_name}")
|
|
323
|
+
self._edf_writer_thread.equipment = f'{self._device_type}_{self._device_no}'
|
|
313
324
|
|
|
314
325
|
self._edf_writer_thread.append(packet.eeg)
|
|
315
326
|
|
|
@@ -329,5 +340,4 @@ class RscEDFHandler(object):
|
|
|
329
340
|
else: onset = 0
|
|
330
341
|
else:
|
|
331
342
|
onset = cur_time - self._first_timestamp
|
|
332
|
-
self._edf_writer_thread.trigger(onset, desc)
|
|
333
|
-
|
|
343
|
+
self._edf_writer_thread.trigger(onset, desc)
|
qlsdk/rsc/command/__init__.py
CHANGED
|
@@ -48,8 +48,9 @@ class DeviceCommand(abc.ABC):
|
|
|
48
48
|
"""构建消息体"""
|
|
49
49
|
return b''
|
|
50
50
|
def pack_header(self, body_len: int) -> bytes:
|
|
51
|
-
device_id = int(self.device.device_id) if self.device and self.device.device_id else 0
|
|
51
|
+
# device_id = int(self.device.device_id) if self.device and self.device.device_id else 0
|
|
52
52
|
device_type = int(self.device.device_type) if self.device and self.device.device_type else 0
|
|
53
|
+
device_id = bytes.fromhex(self.device.device_id)[::-1] if self.device.device_id else bytes.fromhex('00000000')
|
|
53
54
|
|
|
54
55
|
#兼容设计
|
|
55
56
|
b_device_type = None
|
|
@@ -61,7 +62,7 @@ class DeviceCommand(abc.ABC):
|
|
|
61
62
|
DeviceCommand.HEADER_PREFIX
|
|
62
63
|
+ int(2).to_bytes(1, 'little') # pkgType
|
|
63
64
|
+ device_type.to_bytes(1, 'little')
|
|
64
|
-
+ device_id
|
|
65
|
+
+ device_id
|
|
65
66
|
+ (DeviceCommand.HEADER_LEN + body_len + 2).to_bytes(4, 'little') # +1 for checksum
|
|
66
67
|
+ self.cmd_code.to_bytes(2, 'little')
|
|
67
68
|
)
|
|
@@ -204,6 +205,15 @@ class StopAcquisitionCommand(DeviceCommand):
|
|
|
204
205
|
class SetImpedanceParamCommand(DeviceCommand):
|
|
205
206
|
cmd_code = 0x411
|
|
206
207
|
cmd_desc = "设置阻抗测量参数"
|
|
208
|
+
def pack_body(self):
|
|
209
|
+
|
|
210
|
+
return self.device.gen_set_impedance_param()
|
|
211
|
+
def parse_body(self, body):
|
|
212
|
+
result = body[8]
|
|
213
|
+
if result == 0x00:
|
|
214
|
+
logger.info("阻抗测量参数设置成功")
|
|
215
|
+
else:
|
|
216
|
+
logger.warning(f"阻抗测量参数设置失败: {int(result)}")
|
|
207
217
|
|
|
208
218
|
# 启动阻抗测量
|
|
209
219
|
class StartImpedanceCommand(DeviceCommand):
|
|
@@ -211,13 +221,16 @@ class StartImpedanceCommand(DeviceCommand):
|
|
|
211
221
|
cmd_desc = "启动阻抗测量"
|
|
212
222
|
def pack_body(self):
|
|
213
223
|
body = bytes.fromhex('0000')
|
|
214
|
-
body += to_bytes(self.device.
|
|
224
|
+
body += to_bytes(self.device.get_impedance_channels())
|
|
215
225
|
body += bytes.fromhex('0000000000000000') # 8字节占位符
|
|
216
226
|
return body
|
|
217
227
|
|
|
218
228
|
def parse_body(self, body):
|
|
219
|
-
|
|
220
|
-
|
|
229
|
+
result = body[8]
|
|
230
|
+
if result == 0x00:
|
|
231
|
+
logger.info("阻抗测量启动成功")
|
|
232
|
+
else:
|
|
233
|
+
logger.warning(f"阻抗测量启动失败: {int(result)}")
|
|
221
234
|
|
|
222
235
|
|
|
223
236
|
# 停止阻抗测量
|
|
@@ -233,7 +246,7 @@ class StartStimulationCommand(DeviceCommand):
|
|
|
233
246
|
cmd_code = 0x48C
|
|
234
247
|
cmd_desc = "启动刺激"
|
|
235
248
|
def pack_body(self):
|
|
236
|
-
return self.device.
|
|
249
|
+
return self.device.get_stim_param()
|
|
237
250
|
# return bytes.fromhex('01000000000000008813000000000000010000000000000000000140420f00640064000000803f0000010000000000000000000000000000000000000000008813000000000000')
|
|
238
251
|
def parse_body(self, body: bytes):
|
|
239
252
|
# time - 8B
|
|
@@ -243,8 +256,9 @@ class StartStimulationCommand(DeviceCommand):
|
|
|
243
256
|
# error_channel - 8B
|
|
244
257
|
# error_channel= int.from_bytes(body[9:17], 'big')
|
|
245
258
|
channels = to_channels(body[9:17])
|
|
246
|
-
logger.
|
|
247
|
-
self.device
|
|
259
|
+
logger.info(f"通道 {channels} 刺激开始")
|
|
260
|
+
if self.device:
|
|
261
|
+
self.device.trigger(f"通道 {channels} 刺激开始")
|
|
248
262
|
# error_type - 1B
|
|
249
263
|
error_type = body[17]
|
|
250
264
|
|
|
@@ -268,8 +282,9 @@ class StopStimulationNotifyCommand(DeviceCommand):
|
|
|
268
282
|
# error_channel - 8B
|
|
269
283
|
# error_channel= int.from_bytes(body[9:17], 'big')
|
|
270
284
|
channels = to_channels(body[9:17])
|
|
271
|
-
logger.
|
|
272
|
-
self.device
|
|
285
|
+
logger.info(f"通道 {channels} 刺激结束")
|
|
286
|
+
if self.device:
|
|
287
|
+
self.device.trigger(f"通道 {channels} 刺激结束", time)
|
|
273
288
|
# error_type - 1B
|
|
274
289
|
error_type = body[17]
|
|
275
290
|
# 刺激信息
|
|
@@ -297,8 +312,7 @@ class ImpedanceDataCommand(DeviceCommand):
|
|
|
297
312
|
cmd_desc = "阻抗数据"
|
|
298
313
|
|
|
299
314
|
def parse_body(self, body: bytes):
|
|
300
|
-
|
|
301
|
-
packet = ImpedancePacket().transfer(body)
|
|
315
|
+
self.device.produce(body, type="impedance")
|
|
302
316
|
|
|
303
317
|
# 信号数据
|
|
304
318
|
class SignalDataCommand(DeviceCommand):
|
|
@@ -309,11 +323,11 @@ class SignalDataCommand(DeviceCommand):
|
|
|
309
323
|
return super().unpack(payload)
|
|
310
324
|
|
|
311
325
|
def parse_body(self, body: bytes):
|
|
312
|
-
# 解析数据包
|
|
313
|
-
packet = RscPacket()
|
|
314
|
-
packet.transfer(body)
|
|
326
|
+
# # 解析数据包
|
|
327
|
+
# packet = RscPacket()
|
|
328
|
+
# packet.transfer(body)
|
|
315
329
|
# 将数据包传递给设备
|
|
316
|
-
self.device.produce(
|
|
330
|
+
self.device.produce(body)
|
|
317
331
|
|
|
318
332
|
|
|
319
333
|
|
qlsdk/rsc/device/__init__.py
CHANGED