qlsdk2 0.6.0a4__py3-none-any.whl → 0.6.0a6__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/rsc/command/__init__.py +1 -1
- qlsdk/rsc/device/base.py +5 -1
- qlsdk/rsc/device/c256_rs.py +3 -310
- qlsdk/rsc/device/c64_rs.py +2 -304
- qlsdk/rsc/paradigm.py +125 -5
- {qlsdk2-0.6.0a4.dist-info → qlsdk2-0.6.0a6.dist-info}/METADATA +1 -1
- {qlsdk2-0.6.0a4.dist-info → qlsdk2-0.6.0a6.dist-info}/RECORD +9 -9
- {qlsdk2-0.6.0a4.dist-info → qlsdk2-0.6.0a6.dist-info}/WHEEL +0 -0
- {qlsdk2-0.6.0a4.dist-info → qlsdk2-0.6.0a6.dist-info}/top_level.txt +0 -0
qlsdk/rsc/command/__init__.py
CHANGED
|
@@ -233,7 +233,7 @@ class StartStimulationCommand(DeviceCommand):
|
|
|
233
233
|
cmd_code = 0x48C
|
|
234
234
|
cmd_desc = "启动刺激"
|
|
235
235
|
def pack_body(self):
|
|
236
|
-
return self.device.
|
|
236
|
+
return self.device.get_stim_param()
|
|
237
237
|
# return bytes.fromhex('01000000000000008813000000000000010000000000000000000140420f00640064000000803f0000010000000000000000000000000000000000000000008813000000000000')
|
|
238
238
|
def parse_body(self, body: bytes):
|
|
239
239
|
# time - 8B
|
qlsdk/rsc/device/base.py
CHANGED
|
@@ -9,6 +9,7 @@ from qlsdk.core.entity import RscPacket, ImpedancePacket
|
|
|
9
9
|
from qlsdk.core.utils import to_bytes
|
|
10
10
|
from qlsdk.rsc.interface import IDevice, IParser
|
|
11
11
|
from qlsdk.rsc.command import StartImpedanceCommand, StopImpedanceCommand, StartStimulationCommand, StopStimulationCommand, SetAcquisitionParamCommand, StartAcquisitionCommand, StopAcquisitionCommand
|
|
12
|
+
from qlsdk.rsc.paradigm import StimulationParadigm
|
|
12
13
|
from qlsdk.rsc.parser.base import TcpMessageParser
|
|
13
14
|
|
|
14
15
|
class QLBaseDevice(IDevice):
|
|
@@ -91,7 +92,7 @@ class QLBaseDevice(IDevice):
|
|
|
91
92
|
]
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
self.stim_paradigm = None
|
|
95
|
+
self.stim_paradigm: StimulationParadigm = None
|
|
95
96
|
|
|
96
97
|
signal_info = {
|
|
97
98
|
"param" : None,
|
|
@@ -389,6 +390,9 @@ class QLBaseDevice(IDevice):
|
|
|
389
390
|
t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,), daemon=True)
|
|
390
391
|
t.start()
|
|
391
392
|
|
|
393
|
+
def get_stim_param(self) -> bytes:
|
|
394
|
+
return self.stim_paradigm.to_bytes()
|
|
395
|
+
|
|
392
396
|
def _stop_stimulation_trigger(self, duration):
|
|
393
397
|
delay = duration
|
|
394
398
|
while delay > 0:
|
qlsdk/rsc/device/c256_rs.py
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
from multiprocessing import Queue
|
|
3
|
-
from threading import Thread
|
|
4
|
-
from time import sleep, time_ns
|
|
5
|
-
from typing import Any, Dict, Literal
|
|
6
|
-
|
|
7
1
|
from loguru import logger
|
|
8
2
|
from qlsdk.core.entity import C256RSPacket
|
|
9
3
|
from qlsdk.persist import RscEDFHandler
|
|
@@ -18,130 +12,6 @@ class C256RS(QLBaseDevice):
|
|
|
18
12
|
|
|
19
13
|
def __init__(self, socket):
|
|
20
14
|
super().__init__(socket)
|
|
21
|
-
self.socket = socket
|
|
22
|
-
|
|
23
|
-
self._id = None
|
|
24
|
-
|
|
25
|
-
# 设备信息
|
|
26
|
-
self.device_id = None
|
|
27
|
-
self.device_name = None
|
|
28
|
-
self._device_no = None
|
|
29
|
-
self.software_version = None
|
|
30
|
-
self.hardware_version = None
|
|
31
|
-
self.connect_time = None
|
|
32
|
-
self.current_time = None
|
|
33
|
-
# mV
|
|
34
|
-
self.voltage = None
|
|
35
|
-
# %
|
|
36
|
-
self.battery_remain = None
|
|
37
|
-
# %
|
|
38
|
-
self.battery_total = None
|
|
39
|
-
# persist
|
|
40
|
-
self._recording = False
|
|
41
|
-
self._storage_path = None
|
|
42
|
-
self._file_prefix = None
|
|
43
|
-
|
|
44
|
-
# 可设置参数
|
|
45
|
-
# 采集:采样量程、采样率、采样通道
|
|
46
|
-
# 刺激:刺激电流、刺激频率、刺激时间、刺激通道
|
|
47
|
-
# 采样量程(mV):188、375、563、750、1125、2250、4500
|
|
48
|
-
self._sample_range:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 188
|
|
49
|
-
# 采样率(Hz):250、500、1000、2000、4000、8000、16000、32000
|
|
50
|
-
self._sample_rate:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 500
|
|
51
|
-
self._physical_max = self._sample_range * 1000 # 物理最大值(uV)
|
|
52
|
-
self._physical_min = -self._sample_range * 1000 # 物理最小值(uV)
|
|
53
|
-
self._digital_max = 8388607
|
|
54
|
-
self._digital_min = -8388608
|
|
55
|
-
self._physical_range = self._physical_max - self._physical_min
|
|
56
|
-
self._digital_range = 16777215
|
|
57
|
-
self._acq_channels = None
|
|
58
|
-
self._acq_param = {
|
|
59
|
-
"sample_range": 188,
|
|
60
|
-
"sample_rate": 500,
|
|
61
|
-
"channels": [],
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
self._stim_param = {
|
|
65
|
-
"stim_type": 0, # 刺激类型:0-所有通道参数相同, 1: 通道参数不同
|
|
66
|
-
"channels": [],
|
|
67
|
-
"param": [{
|
|
68
|
-
"channel_id": 0, #通道号 从0开始 -- 必填
|
|
69
|
-
"waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
|
|
70
|
-
"current": 1, #电流强度(mA) -- 必填
|
|
71
|
-
"duration": 30, #平稳阶段持续时间(s) -- 必填
|
|
72
|
-
"ramp_up": 5, #上升时间(s) 默认0
|
|
73
|
-
"ramp_down": 5, #下降时间(s) 默认0
|
|
74
|
-
"frequency": 500, #频率(Hz) -- 非直流必填
|
|
75
|
-
"phase_position": 0, #相位 -- 默认0
|
|
76
|
-
"duration_delay": "0", #延迟启动时间(s) -- 默认0
|
|
77
|
-
"pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"channel_id": 1, #通道号 从0开始 -- 必填
|
|
81
|
-
"waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
|
|
82
|
-
"current": 1, #电流强度(mA) -- 必填
|
|
83
|
-
"duration": 30, #平稳阶段持续时间(s) -- 必填
|
|
84
|
-
"ramp_up": 5, #上升时间(s) 默认0
|
|
85
|
-
"ramp_down": 5, #下降时间(s) 默认0
|
|
86
|
-
"frequency": 500, #频率(Hz) -- 非直流必填
|
|
87
|
-
"phase_position": 0, #相位 -- 默认0
|
|
88
|
-
"duration_delay": "0", #延迟启动时间(s) -- 默认0
|
|
89
|
-
"pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
self.stim_paradigm = None
|
|
95
|
-
|
|
96
|
-
signal_info = {
|
|
97
|
-
"param" : None,
|
|
98
|
-
"start_time" : None,
|
|
99
|
-
"finished_time" : None,
|
|
100
|
-
"packet_total" : None,
|
|
101
|
-
"last_packet_time" : None,
|
|
102
|
-
"state" : 0
|
|
103
|
-
}
|
|
104
|
-
stim_info = {
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
Impedance_info = {
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
# 信号采集状态
|
|
111
|
-
# 信号数据包总数(一个信号采集周期内)
|
|
112
|
-
# 信号采集参数
|
|
113
|
-
# 电刺激状态
|
|
114
|
-
# 电刺激开始时间(最近一次)
|
|
115
|
-
# 电刺激结束时间(最近一次)
|
|
116
|
-
# 电刺激参数
|
|
117
|
-
# 启动数据解析线程
|
|
118
|
-
# 数据存储状态
|
|
119
|
-
# 存储目录
|
|
120
|
-
|
|
121
|
-
#
|
|
122
|
-
self.__signal_consumer: Dict[str, Queue[Any]]={}
|
|
123
|
-
self.__impedance_consumer: Dict[str, Queue[Any]]={}
|
|
124
|
-
|
|
125
|
-
# EDF文件处理器
|
|
126
|
-
self._edf_handler = None
|
|
127
|
-
self.storage_enable = True
|
|
128
|
-
|
|
129
|
-
# self._parser: IParser = RSCMessageParser(self)
|
|
130
|
-
# self._parser.start()
|
|
131
|
-
|
|
132
|
-
# 启动数据接收线程
|
|
133
|
-
# self._accept = Thread(target=self.accept)
|
|
134
|
-
# self._accept.daemon = True
|
|
135
|
-
# self._accept.start()
|
|
136
|
-
|
|
137
|
-
@property
|
|
138
|
-
def device_no(self):
|
|
139
|
-
return self._device_no
|
|
140
|
-
|
|
141
|
-
@device_no.setter
|
|
142
|
-
def device_no(self, value: str):
|
|
143
|
-
self._device_no = value
|
|
144
|
-
|
|
145
15
|
|
|
146
16
|
def init_edf_handler(self):
|
|
147
17
|
self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
|
|
@@ -149,191 +19,14 @@ class C256RS(QLBaseDevice):
|
|
|
149
19
|
self._edf_handler.set_device_no(self.device_no)
|
|
150
20
|
self._edf_handler.set_storage_path(self._storage_path)
|
|
151
21
|
self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C256RS')
|
|
22
|
+
logger.debug(f"EDF Handler initialized")
|
|
152
23
|
|
|
153
|
-
@property
|
|
154
|
-
def edf_handler(self):
|
|
155
|
-
if not self.storage_enable:
|
|
156
|
-
return None
|
|
157
|
-
|
|
158
|
-
if self._edf_handler is None:
|
|
159
|
-
self.init_edf_handler()
|
|
160
|
-
|
|
161
|
-
return self._edf_handler
|
|
162
|
-
|
|
163
|
-
@property
|
|
164
|
-
def acq_channels(self):
|
|
165
|
-
if self._acq_channels is None:
|
|
166
|
-
self._acq_channels = [i for i in range(1, 256)]
|
|
167
|
-
return self._acq_channels
|
|
168
|
-
@property
|
|
169
|
-
def sample_range(self):
|
|
170
|
-
return self._sample_range if self._sample_range else 188
|
|
171
|
-
@property
|
|
172
|
-
def sample_rate(self):
|
|
173
|
-
return self._sample_rate if self._sample_rate else 500
|
|
174
|
-
@property
|
|
175
|
-
def resolution(self):
|
|
176
|
-
return 24
|
|
177
|
-
|
|
178
|
-
@property
|
|
179
|
-
def signal_consumers(self):
|
|
180
|
-
return self.__signal_consumer
|
|
181
|
-
|
|
182
|
-
@property
|
|
183
|
-
def impedance_consumers(self):
|
|
184
|
-
return self.__impedance_consumer
|
|
185
|
-
|
|
186
|
-
# 设置记录文件路径
|
|
187
|
-
def set_storage_path(self, path):
|
|
188
|
-
self._storage_path = path
|
|
189
|
-
|
|
190
|
-
# 设置记录文件名称前缀
|
|
191
|
-
def set_file_prefix(self, prefix):
|
|
192
|
-
self._file_prefix = prefix
|
|
193
|
-
|
|
194
|
-
# socket发送数据
|
|
195
|
-
def send(self, data):
|
|
196
|
-
self.socket.sendall(data)
|
|
197
|
-
|
|
198
|
-
# 设置刺激参数
|
|
199
|
-
def set_stim_param(self, param):
|
|
200
|
-
self.stim_paradigm = param
|
|
201
24
|
|
|
202
25
|
def _signal_wrapper(self, body: bytes):
|
|
203
26
|
return C256RSPacket().transfer(body)
|
|
204
27
|
|
|
205
|
-
def
|
|
206
|
-
|
|
207
|
-
msg = StartImpedanceCommand.build(self).pack()
|
|
208
|
-
logger.debug(f"start_impedance message is {msg.hex()}")
|
|
209
|
-
self.socket.sendall(msg)
|
|
210
|
-
|
|
211
|
-
def stop_impedance(self):
|
|
212
|
-
logger.info("停止阻抗测量")
|
|
213
|
-
msg = StopImpedanceCommand.build(self).pack()
|
|
214
|
-
logger.debug(f"stop_impedance message is {msg.hex()}")
|
|
215
|
-
self.socket.sendall(msg)
|
|
216
|
-
|
|
217
|
-
def start_stimulation(self):
|
|
218
|
-
if self.stim_paradigm is None:
|
|
219
|
-
logger.warning("刺激参数未设置,请先设置刺激参数")
|
|
220
|
-
return
|
|
221
|
-
logger.info("启动电刺激")
|
|
222
|
-
msg = StartStimulationCommand.build(self).pack()
|
|
223
|
-
logger.debug(f"start_stimulation message is {msg.hex()}")
|
|
224
|
-
self.socket.sendall(msg)
|
|
225
|
-
t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,))
|
|
226
|
-
t.start()
|
|
227
|
-
|
|
228
|
-
def _stop_stimulation_trigger(self, duration):
|
|
229
|
-
delay = duration
|
|
230
|
-
while delay > 0:
|
|
231
|
-
sleep(1)
|
|
232
|
-
delay -= 1
|
|
233
|
-
logger.info(f"_stop_stimulation_trigger duration: {duration}")
|
|
234
|
-
if self._edf_handler:
|
|
235
|
-
self._edf_handler.trigger("stimulation should be stopped")
|
|
236
|
-
else:
|
|
237
|
-
logger.warning("stop stim trigger fail. no edf writer alive")
|
|
238
|
-
|
|
239
|
-
def stop_stimulation(self):
|
|
240
|
-
logger.info("停止电刺激")
|
|
241
|
-
msg = StopStimulationCommand.pack()
|
|
242
|
-
logger.debug(f"stop_stimulation message is {msg.hex()}")
|
|
243
|
-
self.socket.sendall(msg)
|
|
244
|
-
|
|
245
|
-
# 启动采集
|
|
246
|
-
def start_acquisition(self, recording = True):
|
|
247
|
-
logger.info("启动信号采集")
|
|
248
|
-
self._recording = recording
|
|
249
|
-
# 设置数据采集参数
|
|
250
|
-
param_bytes = SetAcquisitionParamCommand.build(self).pack()
|
|
251
|
-
# 启动数据采集
|
|
252
|
-
start_bytes = StartAcquisitionCommand.build(self).pack()
|
|
253
|
-
msg = param_bytes + start_bytes
|
|
254
|
-
logger.debug(f"start_acquisition message is {msg.hex()}")
|
|
255
|
-
self.socket.sendall(msg)
|
|
256
|
-
|
|
257
|
-
# 停止采集
|
|
258
|
-
def stop_acquisition(self):
|
|
259
|
-
logger.info("停止信号采集")
|
|
260
|
-
msg = StopAcquisitionCommand.build(self).pack()
|
|
261
|
-
logger.debug(f"stop_acquisition message is {msg.hex()}")
|
|
262
|
-
self.socket.sendall(msg)
|
|
263
|
-
if self._edf_handler:
|
|
264
|
-
# 发送结束标识
|
|
265
|
-
self.edf_handler.write(None)
|
|
266
|
-
|
|
267
|
-
# 订阅实时数据
|
|
268
|
-
def subscribe(self, topic:str=None, q : Queue=None, type : Literal["signal","impedance"]="signal"):
|
|
269
|
-
|
|
270
|
-
# 数据队列
|
|
271
|
-
if q is None:
|
|
272
|
-
q = Queue(maxsize=10240000) # 10M缓存
|
|
273
|
-
|
|
274
|
-
# 队列名称
|
|
275
|
-
if topic is None:
|
|
276
|
-
topic = f"{type}_{time_ns()}"
|
|
277
|
-
|
|
278
|
-
# 订阅生理电信号数据
|
|
279
|
-
if type == "signal":
|
|
280
|
-
# topic唯一,用来区分不同的订阅队列(下同)
|
|
281
|
-
if topic in list(self.__signal_consumer.keys()):
|
|
282
|
-
logger.warning(f"exists {type} subscribe of {topic}")
|
|
283
|
-
else:
|
|
284
|
-
self.__signal_consumer[topic] = q
|
|
285
|
-
|
|
286
|
-
# 订阅阻抗数据
|
|
287
|
-
if type == "impedance":
|
|
288
|
-
if topic in list(self.__signal_consumer.keys()):
|
|
289
|
-
logger.warning(f"exists {type} subscribe of {topic}")
|
|
290
|
-
else:
|
|
291
|
-
self.__impedance_consumer[topic] = q
|
|
292
|
-
|
|
293
|
-
return topic, q
|
|
294
|
-
|
|
295
|
-
def trigger(self, desc):
|
|
296
|
-
if self._edf_handler:
|
|
297
|
-
self.edf_handler.trigger(desc)
|
|
298
|
-
else:
|
|
299
|
-
logger.warning("no edf handler, no place to recording trigger")
|
|
300
|
-
|
|
301
|
-
def __str__(self):
|
|
302
|
-
return f'''
|
|
303
|
-
Device:
|
|
304
|
-
Name: {self.device_name},
|
|
305
|
-
Type: {hex(self.device_type) if self.device_type else None},
|
|
306
|
-
ID: {self.device_id if self.device_id else None},
|
|
307
|
-
Software: {self.software_version},
|
|
308
|
-
Hardware: {self.hardware_version},
|
|
309
|
-
Connect Time: {self.connect_time},
|
|
310
|
-
Current Time: {self.current_time},
|
|
311
|
-
Voltage: {str(self.voltage) + "mV" if self.voltage else None},
|
|
312
|
-
Battery Remain: {str(self.battery_remain)+ "%" if self.battery_remain else None},
|
|
313
|
-
Battery Total: {str(self.battery_total) + "%" if self.battery_total else None}
|
|
314
|
-
'''
|
|
315
|
-
|
|
316
|
-
def __repr__(self):
|
|
317
|
-
return f'''
|
|
318
|
-
Device:
|
|
319
|
-
Name: {self.device_name},
|
|
320
|
-
Type: {hex(self.device_type)},
|
|
321
|
-
ID: {self.device_id},
|
|
322
|
-
Software: {self.software_version},
|
|
323
|
-
Hardware: {self.hardware_version},
|
|
324
|
-
Connect Time: {self.connect_time},
|
|
325
|
-
Current Time: {self.current_time},
|
|
326
|
-
Voltage: {self.voltage}mV,
|
|
327
|
-
Battery Remain: {self.battery_remain}%,
|
|
328
|
-
Battery Total: {self.battery_total}%
|
|
329
|
-
'''
|
|
330
|
-
|
|
331
|
-
def __eq__(self, other):
|
|
332
|
-
return self.device_name == other.device_name and self.device_type == other.device_type and self.device_id == other.device_id
|
|
333
|
-
|
|
334
|
-
def __hash__(self):
|
|
335
|
-
return hash((self.device_name, self.device_type, self.device_id))
|
|
336
|
-
|
|
28
|
+
def get_stim_param(self) -> bytes:
|
|
29
|
+
return self.stim_paradigm.to_bytes_c256()
|
|
337
30
|
|
|
338
31
|
DeviceFactory.register(C256RS.device_type, C256RS)
|
|
339
32
|
DeviceFactory.register(C256RS.device_type_001, C256RS)
|
qlsdk/rsc/device/c64_rs.py
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
from multiprocessing import Queue
|
|
3
|
-
from threading import Thread
|
|
4
|
-
from time import sleep, time_ns
|
|
5
|
-
from typing import Any, Dict, Literal
|
|
6
|
-
|
|
7
1
|
from loguru import logger
|
|
8
2
|
from qlsdk.persist import RscEDFHandler
|
|
9
|
-
from qlsdk.rsc.interface import IDevice
|
|
3
|
+
from qlsdk.rsc.interface import IDevice
|
|
10
4
|
from qlsdk.rsc.command import *
|
|
11
|
-
from qlsdk.rsc.parser.base import TcpMessageParser
|
|
12
5
|
from qlsdk.rsc.device.base import QLBaseDevice
|
|
13
6
|
|
|
14
7
|
class C64RS(QLBaseDevice):
|
|
@@ -17,123 +10,6 @@ class C64RS(QLBaseDevice):
|
|
|
17
10
|
|
|
18
11
|
def __init__(self, socket):
|
|
19
12
|
super().__init__(socket)
|
|
20
|
-
self.socket = socket
|
|
21
|
-
|
|
22
|
-
self._id = None
|
|
23
|
-
|
|
24
|
-
# 设备信息
|
|
25
|
-
self.device_id = None
|
|
26
|
-
self.device_name = None
|
|
27
|
-
self._device_no = None
|
|
28
|
-
self.software_version = None
|
|
29
|
-
self.hardware_version = None
|
|
30
|
-
self.connect_time = None
|
|
31
|
-
self.current_time = None
|
|
32
|
-
# mV
|
|
33
|
-
self.voltage = None
|
|
34
|
-
# %
|
|
35
|
-
self.battery_remain = None
|
|
36
|
-
# %
|
|
37
|
-
self.battery_total = None
|
|
38
|
-
# persist
|
|
39
|
-
self._recording = False
|
|
40
|
-
self._storage_path = None
|
|
41
|
-
self._file_prefix = None
|
|
42
|
-
|
|
43
|
-
# 可设置参数
|
|
44
|
-
# 采集:采样量程、采样率、采样通道
|
|
45
|
-
# 刺激:刺激电流、刺激频率、刺激时间、刺激通道
|
|
46
|
-
# 采样量程(mV):188、375、563、750、1125、2250、4500
|
|
47
|
-
self._sample_range:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 188
|
|
48
|
-
# 采样率(Hz):250、500、1000、2000、4000、8000、16000、32000
|
|
49
|
-
self._sample_rate:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 500
|
|
50
|
-
self._acq_channels = None
|
|
51
|
-
self._acq_param = {
|
|
52
|
-
"sample_range": 188,
|
|
53
|
-
"sample_rate": 500,
|
|
54
|
-
"channels": [],
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
self._stim_param = {
|
|
58
|
-
"stim_type": 0, # 刺激类型:0-所有通道参数相同, 1: 通道参数不同
|
|
59
|
-
"channels": [],
|
|
60
|
-
"param": [{
|
|
61
|
-
"channel_id": 0, #通道号 从0开始 -- 必填
|
|
62
|
-
"waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
|
|
63
|
-
"current": 1, #电流强度(mA) -- 必填
|
|
64
|
-
"duration": 30, #平稳阶段持续时间(s) -- 必填
|
|
65
|
-
"ramp_up": 5, #上升时间(s) 默认0
|
|
66
|
-
"ramp_down": 5, #下降时间(s) 默认0
|
|
67
|
-
"frequency": 500, #频率(Hz) -- 非直流必填
|
|
68
|
-
"phase_position": 0, #相位 -- 默认0
|
|
69
|
-
"duration_delay": "0", #延迟启动时间(s) -- 默认0
|
|
70
|
-
"pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"channel_id": 1, #通道号 从0开始 -- 必填
|
|
74
|
-
"waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
|
|
75
|
-
"current": 1, #电流强度(mA) -- 必填
|
|
76
|
-
"duration": 30, #平稳阶段持续时间(s) -- 必填
|
|
77
|
-
"ramp_up": 5, #上升时间(s) 默认0
|
|
78
|
-
"ramp_down": 5, #下降时间(s) 默认0
|
|
79
|
-
"frequency": 500, #频率(Hz) -- 非直流必填
|
|
80
|
-
"phase_position": 0, #相位 -- 默认0
|
|
81
|
-
"duration_delay": "0", #延迟启动时间(s) -- 默认0
|
|
82
|
-
"pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
self.stim_paradigm = None
|
|
88
|
-
|
|
89
|
-
signal_info = {
|
|
90
|
-
"param" : None,
|
|
91
|
-
"start_time" : None,
|
|
92
|
-
"finished_time" : None,
|
|
93
|
-
"packet_total" : None,
|
|
94
|
-
"last_packet_time" : None,
|
|
95
|
-
"state" : 0
|
|
96
|
-
}
|
|
97
|
-
stim_info = {
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
Impedance_info = {
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
# 信号采集状态
|
|
104
|
-
# 信号数据包总数(一个信号采集周期内)
|
|
105
|
-
# 信号采集参数
|
|
106
|
-
# 电刺激状态
|
|
107
|
-
# 电刺激开始时间(最近一次)
|
|
108
|
-
# 电刺激结束时间(最近一次)
|
|
109
|
-
# 电刺激参数
|
|
110
|
-
# 启动数据解析线程
|
|
111
|
-
# 数据存储状态
|
|
112
|
-
# 存储目录
|
|
113
|
-
|
|
114
|
-
#
|
|
115
|
-
self.__signal_consumer: Dict[str, Queue[Any]]={}
|
|
116
|
-
self.__impedance_consumer: Dict[str, Queue[Any]]={}
|
|
117
|
-
|
|
118
|
-
# EDF文件处理器
|
|
119
|
-
self._edf_handler = None
|
|
120
|
-
self.storage_enable = True
|
|
121
|
-
|
|
122
|
-
# self._parser: IParser = TcpMessageParser(self)
|
|
123
|
-
# self._parser.start()
|
|
124
|
-
|
|
125
|
-
# # 启动数据接收线程
|
|
126
|
-
# self._accept = Thread(target=self.accept)
|
|
127
|
-
# self._accept.daemon = True
|
|
128
|
-
# self._accept.start()
|
|
129
|
-
|
|
130
|
-
@property
|
|
131
|
-
def device_no(self):
|
|
132
|
-
return self._device_no
|
|
133
|
-
|
|
134
|
-
@device_no.setter
|
|
135
|
-
def device_no(self, value: str):
|
|
136
|
-
self._device_no = value
|
|
137
13
|
|
|
138
14
|
@classmethod
|
|
139
15
|
def from_parent(cls, parent:IDevice) -> IDevice:
|
|
@@ -149,183 +25,5 @@ class C64RS(QLBaseDevice):
|
|
|
149
25
|
self._edf_handler.set_device_no(self.device_no)
|
|
150
26
|
self._edf_handler.set_storage_path(self._storage_path)
|
|
151
27
|
self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C64RS')
|
|
152
|
-
|
|
153
|
-
@property
|
|
154
|
-
def edf_handler(self):
|
|
155
|
-
if not self.storage_enable:
|
|
156
|
-
return None
|
|
157
|
-
|
|
158
|
-
if self._edf_handler is None:
|
|
159
|
-
self.init_edf_handler()
|
|
160
|
-
|
|
161
|
-
return self._edf_handler
|
|
162
|
-
|
|
163
|
-
@property
|
|
164
|
-
def acq_channels(self):
|
|
165
|
-
if self._acq_channels is None:
|
|
166
|
-
self._acq_channels = [i for i in range(1, 63)]
|
|
167
|
-
return self._acq_channels
|
|
168
|
-
@property
|
|
169
|
-
def sample_range(self):
|
|
170
|
-
return self._sample_range if self._sample_range else 188
|
|
171
|
-
@property
|
|
172
|
-
def sample_rate(self):
|
|
173
|
-
return self._sample_rate if self._sample_rate else 500
|
|
174
|
-
@property
|
|
175
|
-
def resolution(self):
|
|
176
|
-
return 24
|
|
177
|
-
|
|
178
|
-
@property
|
|
179
|
-
def signal_consumers(self):
|
|
180
|
-
return self.__signal_consumer
|
|
181
|
-
|
|
182
|
-
@property
|
|
183
|
-
def impedance_consumers(self):
|
|
184
|
-
return self.__impedance_consumer
|
|
185
|
-
|
|
186
|
-
# 设置记录文件路径
|
|
187
|
-
def set_storage_path(self, path):
|
|
188
|
-
self._storage_path = path
|
|
189
|
-
|
|
190
|
-
# 设置记录文件名称前缀
|
|
191
|
-
def set_file_prefix(self, prefix):
|
|
192
|
-
self._file_prefix = prefix
|
|
193
|
-
|
|
194
|
-
# 接收socket消息
|
|
195
|
-
def accept(self):
|
|
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
|
|
202
|
-
|
|
203
|
-
self._parser.append(data)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
# socket发送数据
|
|
207
|
-
def send(self, data):
|
|
208
|
-
self.socket.sendall(data)
|
|
209
|
-
|
|
210
|
-
# 设置刺激参数
|
|
211
|
-
def set_stim_param(self, param):
|
|
212
|
-
self.stim_paradigm = param
|
|
213
|
-
|
|
214
|
-
# 设置采集参数
|
|
215
|
-
def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
|
|
216
|
-
self._acq_param["channels"] = channels
|
|
217
|
-
self._acq_param["sample_rate"] = sample_rate
|
|
218
|
-
self._acq_param["sample_range"] = sample_range
|
|
219
|
-
self._acq_channels = channels
|
|
220
|
-
self._sample_rate = sample_rate
|
|
221
|
-
self._sample_range = sample_range
|
|
222
|
-
|
|
223
|
-
# 通用配置-TODO
|
|
224
|
-
def set_config(self, key:str, val: str):
|
|
225
|
-
pass
|
|
226
|
-
|
|
227
|
-
def start_impedance(self):
|
|
228
|
-
logger.info("启动阻抗测量")
|
|
229
|
-
msg = StartImpedanceCommand.build(self).pack()
|
|
230
|
-
logger.debug(f"start_impedance message is {msg.hex()}")
|
|
231
|
-
self.socket.sendall(msg)
|
|
232
|
-
|
|
233
|
-
def stop_impedance(self):
|
|
234
|
-
logger.info("停止阻抗测量")
|
|
235
|
-
msg = StopImpedanceCommand.build(self).pack()
|
|
236
|
-
logger.debug(f"stop_impedance message is {msg.hex()}")
|
|
237
|
-
self.socket.sendall(msg)
|
|
238
|
-
|
|
239
|
-
def start_stimulation(self):
|
|
240
|
-
if self.stim_paradigm is None:
|
|
241
|
-
logger.warning("刺激参数未设置,请先设置刺激参数")
|
|
242
|
-
return
|
|
243
|
-
logger.info("启动电刺激")
|
|
244
|
-
msg = StartStimulationCommand.build(self).pack()
|
|
245
|
-
logger.debug(f"start_stimulation message is {msg.hex()}")
|
|
246
|
-
self.socket.sendall(msg)
|
|
247
|
-
t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,))
|
|
248
|
-
t.start()
|
|
249
|
-
|
|
250
|
-
def _stop_stimulation_trigger(self, duration):
|
|
251
|
-
delay = duration
|
|
252
|
-
while delay > 0:
|
|
253
|
-
sleep(1)
|
|
254
|
-
delay -= 1
|
|
255
|
-
logger.info(f"_stop_stimulation_trigger duration: {duration}")
|
|
256
|
-
if self._edf_handler:
|
|
257
|
-
self._edf_handler.trigger("stimulation should be stopped")
|
|
258
|
-
else:
|
|
259
|
-
logger.warning("stop stim trigger fail. no edf writer alive")
|
|
260
|
-
|
|
261
|
-
def stop_stimulation(self):
|
|
262
|
-
logger.info("停止电刺激")
|
|
263
|
-
msg = StopStimulationCommand.pack()
|
|
264
|
-
logger.debug(f"stop_stimulation message is {msg.hex()}")
|
|
265
|
-
self.socket.sendall(msg)
|
|
266
|
-
|
|
267
|
-
# 启动采集
|
|
268
|
-
def start_acquisition(self, recording = True):
|
|
269
|
-
logger.info("启动信号采集")
|
|
270
|
-
self._recording = recording
|
|
271
|
-
# 设置数据采集参数
|
|
272
|
-
param_bytes = SetAcquisitionParamCommand.build(self).pack()
|
|
273
|
-
# 启动数据采集
|
|
274
|
-
start_bytes = StartAcquisitionCommand.build(self).pack()
|
|
275
|
-
msg = param_bytes + start_bytes
|
|
276
|
-
logger.debug(f"start_acquisition message is {msg.hex()}")
|
|
277
|
-
self.socket.sendall(msg)
|
|
278
|
-
|
|
279
|
-
# 停止采集
|
|
280
|
-
def stop_acquisition(self):
|
|
281
|
-
logger.info("停止信号采集")
|
|
282
|
-
msg = StopAcquisitionCommand.build(self).pack()
|
|
283
|
-
logger.debug(f"stop_acquisition message is {msg.hex()}")
|
|
284
|
-
self.socket.sendall(msg)
|
|
285
|
-
if self._edf_handler:
|
|
286
|
-
# 发送结束标识
|
|
287
|
-
self.edf_handler.write(None)
|
|
288
|
-
|
|
289
|
-
def trigger(self, desc):
|
|
290
|
-
if self._edf_handler:
|
|
291
|
-
self.edf_handler.trigger(desc)
|
|
292
|
-
else:
|
|
293
|
-
logger.warning("no edf handler, no place to recording trigger")
|
|
294
|
-
|
|
295
|
-
def __str__(self):
|
|
296
|
-
return f'''
|
|
297
|
-
Device:
|
|
298
|
-
Name: {self.device_name},
|
|
299
|
-
Type: {hex(self.device_type) if self.device_type else None},
|
|
300
|
-
ID: {self.device_id if self.device_id else None},
|
|
301
|
-
Software: {self.software_version},
|
|
302
|
-
Hardware: {self.hardware_version},
|
|
303
|
-
Connect Time: {self.connect_time},
|
|
304
|
-
Current Time: {self.current_time},
|
|
305
|
-
Voltage: {str(self.voltage) + "mV" if self.voltage else None},
|
|
306
|
-
Battery Remain: {str(self.battery_remain)+ "%" if self.battery_remain else None},
|
|
307
|
-
Battery Total: {str(self.battery_total) + "%" if self.battery_total else None}
|
|
308
|
-
'''
|
|
309
|
-
|
|
310
|
-
def __repr__(self):
|
|
311
|
-
return f'''
|
|
312
|
-
Device:
|
|
313
|
-
Name: {self.device_name},
|
|
314
|
-
Type: {hex(self.device_type)},
|
|
315
|
-
ID: {self.device_id},
|
|
316
|
-
Software: {self.software_version},
|
|
317
|
-
Hardware: {self.hardware_version},
|
|
318
|
-
Connect Time: {self.connect_time},
|
|
319
|
-
Current Time: {self.current_time},
|
|
320
|
-
Voltage: {self.voltage}mV,
|
|
321
|
-
Battery Remain: {self.battery_remain}%,
|
|
322
|
-
Battery Total: {self.battery_total}%
|
|
323
|
-
'''
|
|
324
|
-
|
|
325
|
-
def __eq__(self, other):
|
|
326
|
-
return self.device_name == other.device_name and self.device_type == other.device_type and self.device_id == other.device_id
|
|
327
|
-
|
|
328
|
-
def __hash__(self):
|
|
329
|
-
return hash((self.device_name, self.device_type, self.device_id))
|
|
330
|
-
|
|
28
|
+
logger.debug(f"EDF Handler initialized")
|
|
331
29
|
|
qlsdk/rsc/paradigm.py
CHANGED
|
@@ -61,6 +61,22 @@ class StimulationChannel(ABC):
|
|
|
61
61
|
|
|
62
62
|
return result
|
|
63
63
|
|
|
64
|
+
def to_bytes_c256(self):
|
|
65
|
+
result = self.channel_id.to_bytes(1, 'little')
|
|
66
|
+
result += self.wave_form.to_bytes(1, 'little')
|
|
67
|
+
|
|
68
|
+
result += self._to_bytes_c256()
|
|
69
|
+
|
|
70
|
+
result += int(self.delay_time).to_bytes(4, 'little')
|
|
71
|
+
result += int(self.ramp_up * 1000).to_bytes(4, 'little')
|
|
72
|
+
result += int((self.duration + self.ramp_up) * 1000).to_bytes(4, 'little')
|
|
73
|
+
result += int(self.ramp_down * 1000).to_bytes(4, 'little')
|
|
74
|
+
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
def _to_bytes_c256(self):
|
|
78
|
+
return bytes.fromhex("00000000000000000000000000000000000000000000000000000000")
|
|
79
|
+
|
|
64
80
|
def _ext_bytes(self):
|
|
65
81
|
return bytes.fromhex("00000000000000000000000000000000")
|
|
66
82
|
|
|
@@ -99,6 +115,9 @@ class DCStimulation(StimulationChannel):
|
|
|
99
115
|
"delay_time": self.delay_time
|
|
100
116
|
}
|
|
101
117
|
|
|
118
|
+
def _to_bytes_c256(self):
|
|
119
|
+
return int(self.current_max * 1000 ).to_bytes(2, 'little') + bytes.fromhex("0000000000000000000000000000000000000000000000000000")
|
|
120
|
+
|
|
102
121
|
def from_json(self, param):
|
|
103
122
|
pass
|
|
104
123
|
def __str__(self):
|
|
@@ -140,9 +159,19 @@ class SquareWaveStimulation(StimulationChannel):
|
|
|
140
159
|
result += int((self.duration + self.ramp_up) * 1000).to_bytes(4, 'little')
|
|
141
160
|
result += int(self.ramp_down * 1000).to_bytes(4, 'little')
|
|
142
161
|
|
|
143
|
-
return result
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
return result
|
|
163
|
+
|
|
164
|
+
def _to_bytes_c256(self):
|
|
165
|
+
result = int(self.current_max * 1000000 ).to_bytes(4, 'little')
|
|
166
|
+
result += int(self.frequency).to_bytes(2, 'little')
|
|
167
|
+
result += bytes.fromhex("0000")
|
|
168
|
+
result += struct.pack('<f', self.duty_cycle)
|
|
169
|
+
result = int(self.current_min * 1000000 ).to_bytes(4, 'little')
|
|
170
|
+
result += self._ext_bytes()
|
|
171
|
+
return result
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# 刺激模式-正弦
|
|
146
175
|
class ACStimulation(StimulationChannel):
|
|
147
176
|
'''
|
|
148
177
|
channel_id: int, 通道编号,从0开始
|
|
@@ -177,6 +206,15 @@ class ACStimulation(StimulationChannel):
|
|
|
177
206
|
|
|
178
207
|
def from_json(self, param):
|
|
179
208
|
pass
|
|
209
|
+
|
|
210
|
+
def _to_bytes_c256(self):
|
|
211
|
+
result = int(self.current_max * 1000 ).to_bytes(2, 'little')
|
|
212
|
+
result += bytes.fromhex("0000")
|
|
213
|
+
result += struct.pack('<f', self.frequency)
|
|
214
|
+
result += struct.pack('<f', self.phase_position)
|
|
215
|
+
result += self._ext_bytes()
|
|
216
|
+
return result
|
|
217
|
+
|
|
180
218
|
def __str__(self):
|
|
181
219
|
return f"ACStimulation(channel_id={self.channel_id}, waveform={self.waveform}, current={self.current_max}, duration={self.duration}, ramp_up={self.ramp_up}, ramp_down={self.ramp_down}, frequency={self.frequency}, phase_position={self.phase_position}, duration_delay={self.duration_delay})"
|
|
182
220
|
|
|
@@ -230,6 +268,15 @@ class PulseStimulation(StimulationChannel):
|
|
|
230
268
|
|
|
231
269
|
return result
|
|
232
270
|
|
|
271
|
+
def _to_bytes_c256(self):
|
|
272
|
+
result = int(self.current_max * 1000000 ).to_bytes(4, 'little')
|
|
273
|
+
result += int(self.frequency).to_bytes(2, 'little')
|
|
274
|
+
result += bytes.fromhex("0000")
|
|
275
|
+
result += struct.pack('<f', self.duty_cycle)
|
|
276
|
+
result = int(self.current_min * 1000000 ).to_bytes(4, 'little')
|
|
277
|
+
result += self._ext_bytes()
|
|
278
|
+
return result
|
|
279
|
+
|
|
233
280
|
def to_json(self):
|
|
234
281
|
return {
|
|
235
282
|
"channel_id": self.channel_id,
|
|
@@ -254,7 +301,7 @@ class StimulationParadigm(object):
|
|
|
254
301
|
def __init__(self):
|
|
255
302
|
self.channels = None
|
|
256
303
|
self.duration = None
|
|
257
|
-
self.interval_time =
|
|
304
|
+
self.interval_time = 25000
|
|
258
305
|
self.characteristic = 1
|
|
259
306
|
self.mode = 0
|
|
260
307
|
self.repeats = 0
|
|
@@ -287,6 +334,17 @@ class StimulationParadigm(object):
|
|
|
287
334
|
result += channel.to_bytes()
|
|
288
335
|
return result
|
|
289
336
|
|
|
337
|
+
def to_bytes_c256(self):
|
|
338
|
+
result = to_bytes(list(self.channels.keys()), 256)
|
|
339
|
+
result += int(self.duration * 1000).to_bytes(4, 'little')
|
|
340
|
+
result += int(self.interval_time).to_bytes(4, 'little')
|
|
341
|
+
result += int(self.characteristic).to_bytes(4, 'little')
|
|
342
|
+
result += int(self.mode).to_bytes(1, 'little')
|
|
343
|
+
result += int(self.repeats).to_bytes(4, 'little')
|
|
344
|
+
for channel in self.channels.values():
|
|
345
|
+
result += channel.to_bytes()
|
|
346
|
+
return result
|
|
347
|
+
|
|
290
348
|
def to_json(self):
|
|
291
349
|
# Convert the object to JSON for transmission
|
|
292
350
|
return {
|
|
@@ -306,8 +364,70 @@ class StimulationParadigm(object):
|
|
|
306
364
|
def clear(self):
|
|
307
365
|
self.channels = None
|
|
308
366
|
self.duration = None
|
|
309
|
-
self.interval_time =
|
|
367
|
+
self.interval_time = 25000
|
|
310
368
|
self.characteristic = 1
|
|
311
369
|
self.mode = 0
|
|
312
370
|
self.repeats = 0
|
|
313
371
|
|
|
372
|
+
|
|
373
|
+
# 刺激范式
|
|
374
|
+
class C256StimulationParadigm(object):
|
|
375
|
+
def __init__(self):
|
|
376
|
+
self.channels = None
|
|
377
|
+
self.duration = None
|
|
378
|
+
self.interval_time = 0
|
|
379
|
+
self.characteristic = 1
|
|
380
|
+
self.mode = 0
|
|
381
|
+
self.repeats = 0
|
|
382
|
+
|
|
383
|
+
def add_channel(self, channel: StimulationChannel, update=False):
|
|
384
|
+
if self.channels is None:
|
|
385
|
+
self.channels = {}
|
|
386
|
+
channel_id = channel.channel_id + 1
|
|
387
|
+
if channel_id in self.channels.keys():
|
|
388
|
+
logger.warning(f"Channel {channel_id} already exists")
|
|
389
|
+
if update:
|
|
390
|
+
self.channels[channel_id] = channel
|
|
391
|
+
else:
|
|
392
|
+
self.channels[channel_id] = channel
|
|
393
|
+
|
|
394
|
+
# 计算刺激时间
|
|
395
|
+
duration = channel.duration + channel.ramp_up + channel.ramp_down
|
|
396
|
+
if self.duration is None or duration > self.duration:
|
|
397
|
+
self.duration = duration
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def to_bytes(self):
|
|
401
|
+
result = to_bytes(list(self.channels.keys()), 64)
|
|
402
|
+
result += int(self.duration * 1000).to_bytes(4, 'little')
|
|
403
|
+
result += int(self.interval_time).to_bytes(4, 'little')
|
|
404
|
+
result += int(self.characteristic).to_bytes(4, 'little')
|
|
405
|
+
result += int(self.mode).to_bytes(1, 'little')
|
|
406
|
+
result += int(self.repeats).to_bytes(4, 'little')
|
|
407
|
+
for channel in self.channels.values():
|
|
408
|
+
result += channel.to_bytes()
|
|
409
|
+
return result
|
|
410
|
+
|
|
411
|
+
def to_json(self):
|
|
412
|
+
# Convert the object to JSON for transmission
|
|
413
|
+
return {
|
|
414
|
+
"channels": list(self.channels.keys()),
|
|
415
|
+
"duration": self.duration,
|
|
416
|
+
"interval_time": self.interval_time,
|
|
417
|
+
"characteristic": self.characteristic,
|
|
418
|
+
"mode": self.mode,
|
|
419
|
+
"repeats": self.repeats,
|
|
420
|
+
"stim": [channel.to_json() for channel in self.channels.values()]
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
# @staticmethod
|
|
424
|
+
# def from_json(param: Dict[str, Any]):
|
|
425
|
+
# pass
|
|
426
|
+
|
|
427
|
+
def clear(self):
|
|
428
|
+
self.channels = None
|
|
429
|
+
self.duration = None
|
|
430
|
+
self.interval_time = 0
|
|
431
|
+
self.characteristic = 1
|
|
432
|
+
self.mode = 0
|
|
433
|
+
self.repeats = 0
|
|
@@ -37,16 +37,16 @@ qlsdk/rsc/device_manager.py,sha256=1ucd-lzHkNeQPKPzXV6OBkAMqPp_vOcsLyS-9TJ7wRc,4
|
|
|
37
37
|
qlsdk/rsc/discover.py,sha256=ONXN6YWY-OMU0sBoLqqKUyb-8drtAp1g_MvnpzaFvHQ,3124
|
|
38
38
|
qlsdk/rsc/eegion.py,sha256=lxrktO-3Z_MYdFIwc4NxvgLM5AL5kU3UItjH6tsKmHY,11670
|
|
39
39
|
qlsdk/rsc/entity.py,sha256=-fRWFkVWp9d8Y1uh6GiacXC5scdeEKNiNFf3aziGdCE,17751
|
|
40
|
-
qlsdk/rsc/paradigm.py,sha256=
|
|
40
|
+
qlsdk/rsc/paradigm.py,sha256=dYkm0BCpx7tkL-1OE3Io48JgKT0j-lP-n3uC1AcFiWs,17561
|
|
41
41
|
qlsdk/rsc/proxy.py,sha256=9CPdGNGWremwBUh4GvlXAykYB-x_BEPPLqsNvwuwIDE,2736
|
|
42
|
-
qlsdk/rsc/command/__init__.py,sha256=
|
|
42
|
+
qlsdk/rsc/command/__init__.py,sha256=bnnPxn5mChrKCpJbnX718YuFEBDZ8KESYfMIf_8K7J0,12225
|
|
43
43
|
qlsdk/rsc/command/message.py,sha256=nTdG-Vp4MBnltyrgedAWiKD6kzOaPrg58Z_hq6yjhys,12220
|
|
44
44
|
qlsdk/rsc/device/__init__.py,sha256=BzY9lRfssGPUlJ1ys-v3CWNgGihg7mPa2T4X0tl0Vg4,214
|
|
45
45
|
qlsdk/rsc/device/arskindling.py,sha256=owci6MEGjyWqohEXzPdKj_ESeVIZKgO53StVj6Tmi18,15002
|
|
46
|
-
qlsdk/rsc/device/base.py,sha256=
|
|
46
|
+
qlsdk/rsc/device/base.py,sha256=cE4nGsOmw44gU4e1PiJmbv2KuQ0dFIoIIkXXjA4_44A,20239
|
|
47
47
|
qlsdk/rsc/device/c16_rs.py,sha256=BHQRHOnsTMAKgqSXaAS2RjPIklZQAl2CVfe6i_iX-i4,5928
|
|
48
|
-
qlsdk/rsc/device/c256_rs.py,sha256=
|
|
49
|
-
qlsdk/rsc/device/c64_rs.py,sha256=
|
|
48
|
+
qlsdk/rsc/device/c256_rs.py,sha256=EOzNqf4zZwmnzzLyb0KlOXmHhFjPJ3TUVGUDkGgI5Wc,1281
|
|
49
|
+
qlsdk/rsc/device/c64_rs.py,sha256=x8wHdwATKDU34j9vXNEXsNSJg23RAWmAKL8pgIGamG8,1091
|
|
50
50
|
qlsdk/rsc/device/c64s1.py,sha256=L7nKmsoMCGj6GMjHYfYkKgkBtrGfP516kQHQ5I1FAUE,13986
|
|
51
51
|
qlsdk/rsc/device/device_factory.py,sha256=6cPhm3pPGrVXA1s1HePFLjZqmhNI1vOAucFI0VRD_Y0,1317
|
|
52
52
|
qlsdk/rsc/interface/__init__.py,sha256=xeRzIlQSB7ZSf4r5kLfH5cDQLzCyWeJAReG8Xq5nOE0,70
|
|
@@ -70,7 +70,7 @@ qlsdk/sdk/libs/libAr4SDK.dll,sha256=kZp9_DRwPdAJ5OgTFQSqS8tEETxUs7YmmETuBP2g60U,
|
|
|
70
70
|
qlsdk/sdk/libs/libwinpthread-1.dll,sha256=W77ySaDQDi0yxpnQu-ifcU6-uHKzmQpcvsyx2J9j5eg,52224
|
|
71
71
|
qlsdk/x8/__init__.py,sha256=FDpDK7GAYL-g3vzfU9U_V03QzoYoxH9YLm93PjMlANg,4870
|
|
72
72
|
qlsdk/x8m/__init__.py,sha256=cLeUqEEj65qXw4Qa4REyxoLh6T24anSqPaKe9_lR340,634
|
|
73
|
-
qlsdk2-0.6.
|
|
74
|
-
qlsdk2-0.6.
|
|
75
|
-
qlsdk2-0.6.
|
|
76
|
-
qlsdk2-0.6.
|
|
73
|
+
qlsdk2-0.6.0a6.dist-info/METADATA,sha256=xrhOFC2qVtEuKyGCkA_rOjFRGbFGrI0XB-hU1vLrHKo,1882
|
|
74
|
+
qlsdk2-0.6.0a6.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
|
75
|
+
qlsdk2-0.6.0a6.dist-info/top_level.txt,sha256=2CHzn0SY-NIBVyBl07Suh-Eo8oBAQfyNPtqQ_aDatBg,6
|
|
76
|
+
qlsdk2-0.6.0a6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|