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.
@@ -0,0 +1,364 @@
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
+ from loguru import logger
8
+ from qlsdk.persist import RscEDFHandler
9
+ from qlsdk.rsc.interface import IDevice, IParser
10
+ from qlsdk.rsc.command import *
11
+ from qlsdk.rsc.parser.base import TcpMessageParser
12
+ from qlsdk.rsc.device.base import QLBaseDevice
13
+
14
+ class C64RS(QLBaseDevice):
15
+
16
+ device_type = 0x39 # C64RS设备类型标识符
17
+
18
+ def __init__(self, socket):
19
+ self.socket = socket
20
+
21
+ self._id = None
22
+
23
+ # 设备信息
24
+ self.device_id = None
25
+ self.device_name = None
26
+ self._device_no = None
27
+ self.software_version = None
28
+ self.hardware_version = None
29
+ self.connect_time = None
30
+ self.current_time = None
31
+ # mV
32
+ self.voltage = None
33
+ # %
34
+ self.battery_remain = None
35
+ # %
36
+ self.battery_total = None
37
+ # persist
38
+ self._recording = False
39
+ self._storage_path = None
40
+ self._file_prefix = None
41
+
42
+ # 可设置参数
43
+ # 采集:采样量程、采样率、采样通道
44
+ # 刺激:刺激电流、刺激频率、刺激时间、刺激通道
45
+ # 采样量程(mV):188、375、563、750、1125、2250、4500
46
+ self._sample_range:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 188
47
+ # 采样率(Hz):250、500、1000、2000、4000、8000、16000、32000
48
+ self._sample_rate:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 500
49
+ self._physical_max = self._sample_range * 1000 # 物理最大值(uV)
50
+ self._physical_min = -self._sample_range * 1000 # 物理最小值(uV)
51
+ self._digital_max = 8388607
52
+ self._digital_min = -8388608
53
+ self._physical_range = self._physical_max - self._physical_min
54
+ self._digital_range = 16777215
55
+ self._acq_channels = None
56
+ self._acq_param = {
57
+ "sample_range": 188,
58
+ "sample_rate": 500,
59
+ "channels": [],
60
+ }
61
+
62
+ self._stim_param = {
63
+ "stim_type": 0, # 刺激类型:0-所有通道参数相同, 1: 通道参数不同
64
+ "channels": [],
65
+ "param": [{
66
+ "channel_id": 0, #通道号 从0开始 -- 必填
67
+ "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
68
+ "current": 1, #电流强度(mA) -- 必填
69
+ "duration": 30, #平稳阶段持续时间(s) -- 必填
70
+ "ramp_up": 5, #上升时间(s) 默认0
71
+ "ramp_down": 5, #下降时间(s) 默认0
72
+ "frequency": 500, #频率(Hz) -- 非直流必填
73
+ "phase_position": 0, #相位 -- 默认0
74
+ "duration_delay": "0", #延迟启动时间(s) -- 默认0
75
+ "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
76
+ },
77
+ {
78
+ "channel_id": 1, #通道号 从0开始 -- 必填
79
+ "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
80
+ "current": 1, #电流强度(mA) -- 必填
81
+ "duration": 30, #平稳阶段持续时间(s) -- 必填
82
+ "ramp_up": 5, #上升时间(s) 默认0
83
+ "ramp_down": 5, #下降时间(s) 默认0
84
+ "frequency": 500, #频率(Hz) -- 非直流必填
85
+ "phase_position": 0, #相位 -- 默认0
86
+ "duration_delay": "0", #延迟启动时间(s) -- 默认0
87
+ "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
88
+ }
89
+ ]
90
+ }
91
+
92
+ self.stim_paradigm = None
93
+
94
+ signal_info = {
95
+ "param" : None,
96
+ "start_time" : None,
97
+ "finished_time" : None,
98
+ "packet_total" : None,
99
+ "last_packet_time" : None,
100
+ "state" : 0
101
+ }
102
+ stim_info = {
103
+
104
+ }
105
+ Impedance_info = {
106
+
107
+ }
108
+ # 信号采集状态
109
+ # 信号数据包总数(一个信号采集周期内)
110
+ # 信号采集参数
111
+ # 电刺激状态
112
+ # 电刺激开始时间(最近一次)
113
+ # 电刺激结束时间(最近一次)
114
+ # 电刺激参数
115
+ # 启动数据解析线程
116
+ # 数据存储状态
117
+ # 存储目录
118
+
119
+ #
120
+ self.__signal_consumer: Dict[str, Queue[Any]]={}
121
+ self.__impedance_consumer: Dict[str, Queue[Any]]={}
122
+
123
+ # EDF文件处理器
124
+ self._edf_handler = None
125
+ self.storage_enable = True
126
+
127
+ self._parser: IParser = TcpMessageParser(self)
128
+ self._parser.start()
129
+
130
+ # 启动数据接收线程
131
+ self._accept = Thread(target=self.accept)
132
+ self._accept.daemon = True
133
+ self._accept.start()
134
+
135
+ @property
136
+ def device_no(self):
137
+ return self._device_no
138
+
139
+ @device_no.setter
140
+ def device_no(self, value: str):
141
+ self._device_no = value
142
+
143
+ @classmethod
144
+ def from_parent(cls, parent:IDevice) -> IDevice:
145
+ rlt = cls(parent.socket)
146
+ rlt.device_id = parent.device_id
147
+ rlt._device_no = parent.device_no
148
+ return rlt
149
+
150
+
151
+ def init_edf_handler(self):
152
+ self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
153
+ self._edf_handler.set_device_type(self.device_type)
154
+ self._edf_handler.set_device_no(self.device_name)
155
+ self._edf_handler.set_storage_path(self._storage_path)
156
+ self._edf_handler.set_file_prefix(self._file_prefix)
157
+
158
+ @property
159
+ def edf_handler(self):
160
+ if not self.storage_enable:
161
+ return None
162
+
163
+ if self._edf_handler is None:
164
+ self.init_edf_handler()
165
+
166
+ return self._edf_handler
167
+
168
+ @property
169
+ def acq_channels(self):
170
+ if self._acq_channels is None:
171
+ self._acq_channels = [i for i in range(1, 63)]
172
+ return self._acq_channels
173
+ @property
174
+ def sample_range(self):
175
+ return self._sample_range if self._sample_range else 188
176
+ @property
177
+ def sample_rate(self):
178
+ return self._sample_rate if self._sample_rate else 500
179
+ @property
180
+ def resolution(self):
181
+ return 24
182
+
183
+ @property
184
+ def signal_consumers(self):
185
+ return self.__signal_consumer
186
+
187
+ @property
188
+ def impedance_consumers(self):
189
+ return self.__impedance_consumer
190
+
191
+ # 设置记录文件路径
192
+ def set_storage_path(self, path):
193
+ self._storage_path = path
194
+
195
+ # 设置记录文件名称前缀
196
+ def set_file_prefix(self, prefix):
197
+ self._file_prefix = prefix
198
+
199
+ # 接收socket消息
200
+ def accept(self):
201
+ while True:
202
+ # 缓冲去4M
203
+ data = self.socket.recv(4096*1024)
204
+ if not data:
205
+ logger.warning(f"设备{self.device_name}连接结束")
206
+ break
207
+
208
+ self._parser.append(data)
209
+
210
+
211
+ # socket发送数据
212
+ def send(self, data):
213
+ self.socket.sendall(data)
214
+
215
+ # 设置刺激参数
216
+ def set_stim_param(self, param):
217
+ self.stim_paradigm = param
218
+
219
+ # 设置采集参数
220
+ def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
221
+ self._acq_param["channels"] = channels
222
+ self._acq_param["sample_rate"] = sample_rate
223
+ self._acq_param["sample_range"] = sample_range
224
+ self._acq_channels = channels
225
+ self._sample_rate = sample_rate
226
+ self._sample_range = sample_range
227
+
228
+ # 通用配置-TODO
229
+ def set_config(self, key:str, val: str):
230
+ pass
231
+
232
+ def start_impedance(self):
233
+ logger.info("启动阻抗测量")
234
+ msg = StartImpedanceCommand.build(self).pack()
235
+ logger.debug(f"start_impedance message is {msg.hex()}")
236
+ self.socket.sendall(msg)
237
+
238
+ def stop_impedance(self):
239
+ logger.info("停止阻抗测量")
240
+ msg = StopImpedanceCommand.build(self).pack()
241
+ logger.debug(f"stop_impedance message is {msg.hex()}")
242
+ self.socket.sendall(msg)
243
+
244
+ def start_stimulation(self):
245
+ if self.stim_paradigm is None:
246
+ logger.warning("刺激参数未设置,请先设置刺激参数")
247
+ return
248
+ logger.info("启动电刺激")
249
+ msg = StartStimulationCommand.build(self).pack()
250
+ logger.debug(f"start_stimulation message is {msg.hex()}")
251
+ self.socket.sendall(msg)
252
+ t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,))
253
+ t.start()
254
+
255
+ def _stop_stimulation_trigger(self, duration):
256
+ delay = duration
257
+ while delay > 0:
258
+ sleep(1)
259
+ delay -= 1
260
+ logger.info(f"_stop_stimulation_trigger duration: {duration}")
261
+ if self._edf_handler:
262
+ self._edf_handler.trigger("stimulation should be stopped")
263
+ else:
264
+ logger.warning("stop stim trigger fail. no edf writer alive")
265
+
266
+ def stop_stimulation(self):
267
+ logger.info("停止电刺激")
268
+ msg = StopStimulationCommand.pack()
269
+ logger.debug(f"stop_stimulation message is {msg.hex()}")
270
+ self.socket.sendall(msg)
271
+
272
+ # 启动采集
273
+ def start_acquisition(self, recording = True):
274
+ logger.info("启动信号采集")
275
+ self._recording = recording
276
+ # 设置数据采集参数
277
+ param_bytes = SetAcquisitionParamCommand.build(self).pack()
278
+ # 启动数据采集
279
+ start_bytes = StartAcquisitionCommand.build(self).pack()
280
+ msg = param_bytes + start_bytes
281
+ logger.debug(f"start_acquisition message is {msg.hex()}")
282
+ self.socket.sendall(msg)
283
+
284
+ # 停止采集
285
+ def stop_acquisition(self):
286
+ logger.info("停止信号采集")
287
+ msg = StopAcquisitionCommand.build(self).pack()
288
+ logger.debug(f"stop_acquisition message is {msg.hex()}")
289
+ self.socket.sendall(msg)
290
+ if self._edf_handler:
291
+ # 发送结束标识
292
+ self.edf_handler.write(None)
293
+
294
+ # 订阅实时数据
295
+ def subscribe(self, topic:str=None, q : Queue=None, type : Literal["signal","impedance"]="signal"):
296
+
297
+ # 数据队列
298
+ if q is None:
299
+ q = Queue(maxsize=1000)
300
+
301
+ # 队列名称
302
+ if topic is None:
303
+ topic = f"{type}_{time_ns()}"
304
+
305
+ # 订阅生理电信号数据
306
+ if type == "signal":
307
+ # topic唯一,用来区分不同的订阅队列(下同)
308
+ if topic in list(self.__signal_consumer.keys()):
309
+ logger.warning(f"exists {type} subscribe of {topic}")
310
+ else:
311
+ self.__signal_consumer[topic] = q
312
+
313
+ # 订阅阻抗数据
314
+ if type == "impedance":
315
+ if topic in list(self.__signal_consumer.keys()):
316
+ logger.warning(f"exists {type} subscribe of {topic}")
317
+ else:
318
+ self.__impedance_consumer[topic] = q
319
+
320
+ return topic, q
321
+
322
+ def trigger(self, desc):
323
+ if self._edf_handler:
324
+ self.edf_handler.trigger(desc)
325
+ else:
326
+ logger.warning("no edf handler, no place to recording trigger")
327
+
328
+ def __str__(self):
329
+ return f'''
330
+ Device:
331
+ Name: {self.device_name},
332
+ Type: {hex(self.device_type) if self.device_type else None},
333
+ ID: {self.device_id if self.device_id else None},
334
+ Software: {self.software_version},
335
+ Hardware: {self.hardware_version},
336
+ Connect Time: {self.connect_time},
337
+ Current Time: {self.current_time},
338
+ Voltage: {str(self.voltage) + "mV" if self.voltage else None},
339
+ Battery Remain: {str(self.battery_remain)+ "%" if self.battery_remain else None},
340
+ Battery Total: {str(self.battery_total) + "%" if self.battery_total else None}
341
+ '''
342
+
343
+ def __repr__(self):
344
+ return f'''
345
+ Device:
346
+ Name: {self.device_name},
347
+ Type: {hex(self.device_type)},
348
+ ID: {self.device_id},
349
+ Software: {self.software_version},
350
+ Hardware: {self.hardware_version},
351
+ Connect Time: {self.connect_time},
352
+ Current Time: {self.current_time},
353
+ Voltage: {self.voltage}mV,
354
+ Battery Remain: {self.battery_remain}%,
355
+ Battery Total: {self.battery_total}%
356
+ '''
357
+
358
+ def __eq__(self, other):
359
+ return self.device_name == other.device_name and self.device_type == other.device_type and self.device_id == other.device_id
360
+
361
+ def __hash__(self):
362
+ return hash((self.device_name, self.device_type, self.device_id))
363
+
364
+
@@ -0,0 +1,29 @@
1
+ from typing import Dict, Type
2
+ from qlsdk.rsc.interface import IDevice
3
+
4
+ from qlsdk.rsc.device.c64_rs import C64RS
5
+
6
+ from loguru import logger
7
+
8
+ class DeviceFactory(object):
9
+ """Registry for device implementations"""
10
+ _devices: Dict[str, Type[IDevice]] = {}
11
+
12
+ @classmethod
13
+ def register(cls, device_type: int, device: Type[IDevice]):
14
+ cls._devices[device_type] = device
15
+
16
+ @classmethod
17
+ def create_device(cls, device: IDevice) -> Type[IDevice]:
18
+ logger.debug(f"Creating device for device type: {hex(device.device_type)}, support types: {cls._devices.keys()}")
19
+ if device.device_type not in cls._devices.keys():
20
+ logger.warning(f"不支持的设备类型: {hex(device.device_type)}")
21
+ raise ValueError(f"Unsupported device type: {hex(device.device_type)}")
22
+
23
+ instance = cls._devices[device.device_type]
24
+ return instance.from_parent(device) if hasattr(instance, 'from_parent') else instance(device.socket)
25
+
26
+ # Register the C64RS device with the DeviceFactory
27
+ DeviceFactory.register(C64RS.device_type, C64RS)
28
+
29
+
qlsdk/rsc/entity.py CHANGED
@@ -2,7 +2,7 @@ from multiprocessing import Queue, Process
2
2
  from typing import Any, Dict, Literal
3
3
  from threading import Thread
4
4
  from loguru import logger
5
- from time import time_ns
5
+ from time import time_ns, sleep
6
6
 
7
7
  from qlsdk.core import *
8
8
  from qlsdk.core.device import BaseDevice
@@ -26,6 +26,10 @@ class QLDevice(BaseDevice):
26
26
  self.battery_remain = None
27
27
  # %
28
28
  self.battery_total = None
29
+ # persist
30
+ self._recording = False
31
+ self._storage_path = None
32
+ self._file_prefix = None
29
33
 
30
34
  # 可设置参数
31
35
  # 采集:采样量程、采样率、采样通道
@@ -34,6 +38,12 @@ class QLDevice(BaseDevice):
34
38
  self._sample_range:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 188
35
39
  # 采样率(Hz):250、500、1000、2000、4000、8000、16000、32000
36
40
  self._sample_rate:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 500
41
+ self._physical_max = 188000
42
+ self._physical_min = -188000
43
+ self._digital_max = 8388607
44
+ self._digital_min = -8388608
45
+ self._physical_range = 376000
46
+ self._digital_range = 16777215
37
47
  self._acq_channels = None
38
48
  self._acq_param = {
39
49
  "sample_range": 188,
@@ -115,8 +125,17 @@ class QLDevice(BaseDevice):
115
125
  self._accept.start()
116
126
 
117
127
  def init_edf_handler(self):
118
- self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range, - self.sample_range, - 2 >> 24, 2 >> 24 - 1, self.resolution)
119
-
128
+ self._edf_handler = RscEDFHandler(self.sample_rate, self._physical_max , self._physical_min, self.resolution)
129
+ self._edf_handler.set_device_type(self.device_type)
130
+ self._edf_handler.set_device_no(self.device_name)
131
+ self._edf_handler.set_storage_path(self._storage_path)
132
+ self._edf_handler.set_file_prefix(self._file_prefix)
133
+
134
+ # eeg数字值转物理值
135
+ def eeg2phy(self, digital:int):
136
+ # 向量化计算(自动支持广播)
137
+ return ((digital - self._digital_min) / self._digital_range) * self._physical_range + self._physical_min
138
+
120
139
  @property
121
140
  def edf_handler(self):
122
141
  if not self.storage_enable:
@@ -127,17 +146,22 @@ class QLDevice(BaseDevice):
127
146
 
128
147
  return self._edf_handler
129
148
 
149
+ # 采集通道列表, 从1开始
130
150
  @property
131
151
  def acq_channels(self):
132
152
  if self._acq_channels is None:
133
153
  self._acq_channels = [i for i in range(1, 63)]
134
154
  return self._acq_channels
155
+
156
+ # 量程范围
135
157
  @property
136
158
  def sample_range(self):
159
+
137
160
  return self._sample_range if self._sample_range else 188
138
161
  @property
139
162
  def sample_rate(self):
140
163
  return self._sample_rate if self._sample_rate else 500
164
+
141
165
  @property
142
166
  def resolution(self):
143
167
  return 24
@@ -150,13 +174,17 @@ class QLDevice(BaseDevice):
150
174
  def impedance_consumers(self):
151
175
  return self.__impedance_consumer
152
176
 
177
+ # 设置记录文件路径
153
178
  def set_storage_path(self, path):
154
- pass
179
+ self._storage_path = path
180
+
181
+ # 设置记录文件名称前缀
182
+ def set_file_prefix(self, prefix):
183
+ self._file_prefix = prefix
155
184
 
156
185
  def accept(self):
157
186
  while True:
158
187
  data = self.socket.recv(4096*1024)
159
- # logger.debug(f"QLDevice接收到数据: {data.hex()}")
160
188
  if not data:
161
189
  logger.warning(f"设备{self.device_name}连接结束")
162
190
  break
@@ -175,35 +203,31 @@ class QLDevice(BaseDevice):
175
203
  self.stim_paradigm = param
176
204
 
177
205
  # 设置采集参数
178
- def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
206
+ def set_acq_param(self, channels, sample_rate:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 500, sample_range:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 188):
179
207
  self._acq_param["channels"] = channels
180
208
  self._acq_param["sample_rate"] = sample_rate
181
209
  self._acq_param["sample_range"] = sample_range
182
210
  self._acq_channels = channels
183
211
  self._sample_rate = sample_rate
184
212
  self._sample_range = sample_range
213
+ # 根据量程更新信号物理值范围
214
+ self._physical_max = self._sample_range * 1000
215
+ self._physical_min = -self._sample_range * 1000
216
+ self._physical_range = self._physical_max - self._physical_min
185
217
 
186
- # 通用配置
218
+ # 通用配置-TODO
187
219
  def set_config(self, key:str, val: str):
188
220
  pass
189
221
 
190
- def set_impedance_param(self):
191
- channels = bytes.fromhex("FFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000")
192
- sample_rate = 1000
193
- sample_len = 300
194
- resolution = self.resolution
195
-
196
222
  def start_impedance(self):
197
223
  logger.info("启动阻抗测量")
198
224
  msg = StartImpedanceCommand.build(self).pack()
199
- # msg = bytes.fromhex("5aa50239320013243f0000001104ffffffffffffffff000000000000000000000000000000000000000000000000e8030000fa00000010000164000000a11e5aa50239320013243a00000012040000000000000000000000000000000000000000000000000000000000000000000001000000000000000325")
200
225
  logger.debug(f"start_impedance message is {msg.hex()}")
201
226
  self.socket.sendall(msg)
202
227
 
203
228
  def stop_impedance(self):
204
229
  logger.info("停止阻抗测量")
205
230
  msg = StopImpedanceCommand.build(self).pack()
206
- # msg = bytes.fromhex("5aa5023932001324100000001304e9df")
207
231
  logger.debug(f"stop_impedance message is {msg.hex()}")
208
232
  self.socket.sendall(msg)
209
233
 
@@ -212,20 +236,33 @@ class QLDevice(BaseDevice):
212
236
  logger.warning("刺激参数未设置,请先设置刺激参数")
213
237
  return
214
238
  logger.info("启动电刺激")
215
- # conf = SetStimulationParamCommand.build(self).pack()
216
239
  msg = StartStimulationCommand.build(self).pack()
217
240
  logger.debug(f"start_stimulation message is {msg.hex()}")
218
241
  self.socket.sendall(msg)
242
+ t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,))
243
+ t.start()
244
+
245
+ def _stop_stimulation_trigger(self, duration):
246
+ delay = duration
247
+ while delay > 0:
248
+ sleep(1)
249
+ delay -= 1
250
+ logger.info(f"_stop_stimulation_trigger duration: {duration}")
251
+ if self._edf_handler:
252
+ self._edf_handler.trigger("stimulation should be stopped")
253
+ else:
254
+ logger.warning("trigger fail for 'stop stim'. no recording file to write")
219
255
 
220
256
  def stop_stimulation(self):
221
257
  logger.info("停止电刺激")
222
- msg = StopStimulationCommand.pack()
258
+ msg = StopStimulationCommand.build().pack()
223
259
  logger.debug(f"stop_stimulation message is {msg.hex()}")
224
260
  self.socket.sendall(msg)
225
261
 
226
262
  # 启动采集
227
- def start_acquisition(self):
263
+ def start_acquisition(self, recording = True):
228
264
  logger.info("启动信号采集")
265
+ self._recording = recording
229
266
  # 设置数据采集参数
230
267
  param_bytes = SetAcquisitionParamCommand.build(self).pack()
231
268
  # 启动数据采集
@@ -238,11 +275,11 @@ class QLDevice(BaseDevice):
238
275
  def stop_acquisition(self):
239
276
  logger.info("停止信号采集")
240
277
  msg = StopAcquisitionCommand.build(self).pack()
241
- logger.debug(f"stop_acquisition message is {msg}")
278
+ logger.debug(f"stop_acquisition message is {msg.hex()}")
242
279
  self.socket.sendall(msg)
243
280
  if self._edf_handler:
244
281
  # 发送结束标识
245
- self.edf_handler.append(None)
282
+ self.edf_handler.write(None)
246
283
 
247
284
  # 订阅实时数据
248
285
  def subscribe(self, topic:str=None, q : Queue=None, type : Literal["signal","impedance"]="signal"):
@@ -271,6 +308,12 @@ class QLDevice(BaseDevice):
271
308
  self.__impedance_consumer[topic] = q
272
309
 
273
310
  return topic, q
311
+
312
+ def trigger(self, desc):
313
+ if self._edf_handler:
314
+ self.edf_handler.trigger(desc)
315
+ else:
316
+ logger.warning("no edf handler, no place to recording trigger")
274
317
 
275
318
  def __str__(self):
276
319
  return f'''
@@ -326,10 +369,6 @@ class RSC64R(QLDevice):
326
369
  class ARSKindling(QLDevice):
327
370
  def __init__(self, socket):
328
371
  super().__init__(socket)
329
-
330
-
331
-
332
-
333
372
 
334
373
  class DeviceParser(object):
335
374
  def __init__(self, device : QLDevice):
@@ -0,0 +1,3 @@
1
+ from .device import *
2
+ from .parser import *
3
+ from .handler import *
@@ -0,0 +1,10 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ class ICommand(ABC):
4
+ def __init__(self):
5
+ super().__init__()
6
+
7
+
8
+ @abstractmethod
9
+ def append(self, buffer):
10
+ pass